diff --git a/cobigen/cobigen-core-api/pom.xml b/cobigen/cobigen-core-api/pom.xml index 7f8555d058..380b6e4345 100644 --- a/cobigen/cobigen-core-api/pom.xml +++ b/cobigen/cobigen-core-api/pom.xml @@ -27,7 +27,7 @@ com.google.guava guava - + org.zeroturnaround @@ -40,11 +40,32 @@ core-test test + commons-io commons-io test + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.squareup.okhttp3 + okhttp + + + + + com.github.tomakehurst + wiremock-standalone + 2.27.2 + test + diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/constants/MavenSearchRepositoryConstants.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/constants/MavenSearchRepositoryConstants.java new file mode 100644 index 0000000000..5fa96b47ad --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/constants/MavenSearchRepositoryConstants.java @@ -0,0 +1,58 @@ +package com.devonfw.cobigen.api.constants; + +/** + * Constants needed for handling the maven search REST APIs + */ +public class MavenSearchRepositoryConstants { + + /** + * Maven repository URL + */ + public static String MAVEN_REPOSITORY_URL = "https://search.maven.org"; + + /** + * Maven repository download link + */ + public static String MAVEN_REPOSITORY_DOWNLOAD_LINK = "https://repo1.maven.org/maven2"; + + /** + * Maven target link + */ + public static String MAVEN_TARGET_LINK = "solrsearch/select"; + + /** + * Nexus2 repository URL + */ + public static String NEXUS2_REPOSITORY_URL = "https://s01.oss.sonatype.org"; + + /** + * Nexus2 repository link + */ + public static String NEXUS2_REPOSITORY_LINK = "service/local/repositories/releases/content"; + + /** + * Nexus2 target link + */ + public static String NEXUS2_TARGET_LINK = "service/local/lucene/search"; + + /** + * Nexus3 target link + */ + public static String NEXUS3_TARGET_LINK = "service/rest/v1/search"; + + /** + * Nexus3 repository URL + */ + public static String NEXUS3_REPOSITORY_URL = ""; + + /** + * Jfrog repository URL + */ + public static String JFROG_REPOSITORY_URL = "http://localhost:8082/artifactory"; + + /** + * Jfrog target link + */ + public static String JFROG_TARGET_LINK = "artifactory/api/search/gavc"; + +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/constants/MavenSearchRepositoryType.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/constants/MavenSearchRepositoryType.java new file mode 100644 index 0000000000..e5c8733d5f --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/constants/MavenSearchRepositoryType.java @@ -0,0 +1,28 @@ +package com.devonfw.cobigen.api.constants; + +/** + * Maven search repository types used to identify and name the available search REST API types (add new search + * repository types/versions here) + */ +public enum MavenSearchRepositoryType { + + /** + * Nexus2 search repository type + */ + NEXUS2, + + /** + * Nexus3 search repository type + */ + NEXUS3, + + /** + * Maven search repository type + */ + MAVEN, + + /** + * Jfrog search repository type + */ + JFROG +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/exception/RestSearchResponseException.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/exception/RestSearchResponseException.java new file mode 100644 index 0000000000..e4a1b6e208 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/exception/RestSearchResponseException.java @@ -0,0 +1,40 @@ +package com.devonfw.cobigen.api.exception; + +/** Exception to indicate that the REST search API encountered a problem while accessing the server. */ +public class RestSearchResponseException extends CobiGenRuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Creates a new {@link RestSearchResponseException} with the given message + * + * @param message error message of the exception + */ + public RestSearchResponseException(String message) { + + super(message); + } + + /** + * Creates a new {@link RestSearchResponseException} with the specified message and the causing {@link Throwable} + * + * @param message describing the exception + * @param cause the causing Throwable + */ + public RestSearchResponseException(String message, Throwable cause) { + + super(message, cause); + } + + /** + * Creates a new {@link RestSearchResponseException} with the specified message and the causing {@link Throwable} + * + * @param message describing the exception + * @param statusCode status code causing the {@link RestSearchResponseException} or null if not available + */ + public RestSearchResponseException(String message, String statusCode) { + + super(message + statusCode); + } + +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/MavenUtil.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/MavenUtil.java index a50d684117..69f96c4e50 100644 --- a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/MavenUtil.java +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/MavenUtil.java @@ -27,6 +27,9 @@ import com.devonfw.cobigen.api.constants.MavenConstants; import com.devonfw.cobigen.api.exception.CobiGenRuntimeException; +import com.devonfw.cobigen.api.exception.RestSearchResponseException; +import com.devonfw.cobigen.api.util.to.SearchResponseFactory; +import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.collect.Lists; import com.google.common.hash.Hashing; import com.google.common.io.ByteSource; @@ -340,4 +343,27 @@ public static Path getProjectRoot(Path inputFile, boolean topLevel) { LOG.debug("Project root could not be found."); return null; } + + /** + * Retrieves a list of download URLs by groupId from the specified repository search REST API using authentication + * with bearer token + * + * @param baseUrl String of the repository server URL + * @param groupId the groupId to search for + * @param authToken bearer token to use for authentication + * @return List of artifact download URLS or null if an error occurred + */ + public static List retrieveMavenArtifactsByGroupId(String baseUrl, String groupId, String authToken) { + + try { + + return SearchResponseFactory.searchArtifactDownloadLinks(baseUrl, groupId, authToken); + } catch (RestSearchResponseException | JsonProcessingException | MalformedURLException e) { + LOG.error("Unable to get artifacts from {} by groupId {}", baseUrl, groupId, e); + // TODO: Handle Eclipse, CLI and MavenPlugin here (f.e. with a new Exception) + return null; + } + + } + } diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/AbstractSearchResponse.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/AbstractSearchResponse.java new file mode 100644 index 0000000000..eb00dba93a --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/AbstractSearchResponse.java @@ -0,0 +1,164 @@ +package com.devonfw.cobigen.api.util.to; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryType; +import com.devonfw.cobigen.api.exception.RestSearchResponseException; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +/** + * This interface should be inherited for all maven REST search API responses to properly convert {@link JsonProperty} + * from responses to valid download URLs + */ +public abstract class AbstractSearchResponse { + + /** Logger instance. */ + @JsonIgnore + private static final Logger LOG = LoggerFactory.getLogger(AbstractSearchResponse.class); + + /** + * @return the {@link MavenSearchRepositoryType} type + */ + public abstract MavenSearchRepositoryType getRepositoryType(); + + /** + * Creates a list of download URLs + * + * @return List of download links + * @throws MalformedURLException if an URL was not valid + */ + public abstract List retrieveDownloadURLs() throws MalformedURLException; + + /** + * Removes duplicates from list of download URLs + * + * @param downloadUrls list of download URLs + * @return List of download links + * @throws MalformedURLException if an URL was not valid + */ + public List removeDuplicatedDownloadURLs(List downloadUrls) throws MalformedURLException { + + return downloadUrls.stream().distinct().collect(Collectors.toList()); + } + + /** + * Retrieves the json response from a repository URL and a group ID + * + * @param repositoryUrl URL of the repository + * @param groupId to search for + * @return String of json response + * @throws RestSearchResponseException if the request did not return status 200 + */ + public String retrieveJsonResponse(String repositoryUrl, String groupId) throws RestSearchResponseException { + + return retrieveJsonResponse(repositoryUrl, groupId, null); + } + + /** + * Retrieves the json response from a repository URL, a group ID and a bearer authentication token + * + * @param repositoryUrl URL of the repository + * @param groupId to search for + * @param authToken bearer token to use for authentication + * @return String of json response + * @throws RestSearchResponseException if the request did not return status 200 + */ + public abstract String retrieveJsonResponse(String repositoryUrl, String groupId, String authToken) + throws RestSearchResponseException; + + /** + * Creates a @WebTarget with provided authentication token + * + * @param targetLink link to get response from + * @param token bearer token to use for authentication + * @return Request to use as resource + */ + private static Request bearerAuthenticationWithOAuth2AtClientLevel(String targetLink, String token) { + + return new Request.Builder().url(targetLink).addHeader("Authorization", "Bearer " + token).build(); + + } + + /** + * Retrieves a json response by given REST API target link using bearer authentication token + * + * @param targetLink link to get response from + * @param authToken bearer token to use for authentication + * @param searchRepositoryType the type of the search repository + * @return String of json response + * @throws RestSearchResponseException if the returned status code was not 200 OK + */ + public static String retrieveJsonResponseWithAuthenticationToken(String targetLink, String authToken, + MavenSearchRepositoryType searchRepositoryType) throws RestSearchResponseException { + + LOG.info("Starting {} search REST API request with URL: {}.", searchRepositoryType, targetLink); + + OkHttpClient httpClient = new OkHttpClient().newBuilder().connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS).callTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS) + .retryOnConnectionFailure(true).build(); + String jsonResponse = ""; + + try { + Response response = null; + + if (authToken != null) { + response = httpClient.newCall(bearerAuthenticationWithOAuth2AtClientLevel(targetLink, authToken)).execute(); + } else { + response = httpClient.newCall(new Request.Builder().url(targetLink).get().build()).execute(); + } + + if (response != null) { + int status = response.code(); + if (status == 200 || status == 201 || status == 204) { + jsonResponse = response.body().string(); + } else { + throw new RestSearchResponseException("The search REST API returned the unexpected status code: ", + String.valueOf(response.code())); + } + } + + } catch (IOException e) { + throw new RestSearchResponseException("Unable to send or receive the message from the service", e); + } catch (IllegalArgumentException e) { + throw new RestSearchResponseException("The target URL was faulty.", e); + } + + return jsonResponse; + + } + + /** + * Creates a download link (concatenates maven repository link with groupId, artifact and version) + * + * @param mavenRepo link to the maven repository to use + * @param groupId for the download link + * @param artifactId for the download link + * @param version for the download link + * @param fileEnding file ending for the download link + * @return concatenated download link + * @throws MalformedURLException if the URL was not valid + */ + protected static URL createDownloadLink(String mavenRepo, String groupId, String artifactId, String version, + String fileEnding) throws MalformedURLException { + + String parsedGroupId = groupId.replace(".", "/"); + String downloadFile = artifactId + "-" + version + fileEnding; + String downloadLink = mavenRepo + "/" + parsedGroupId + "/" + artifactId + "/" + version + "/" + downloadFile; + URL url = new URL(downloadLink); + return url; + } + +} \ No newline at end of file diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/SearchResponseFactory.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/SearchResponseFactory.java new file mode 100644 index 0000000000..4fd420d474 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/SearchResponseFactory.java @@ -0,0 +1,72 @@ +package com.devonfw.cobigen.api.util.to; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.cobigen.api.exception.RestSearchResponseException; +import com.devonfw.cobigen.api.util.to.jfrog.JfrogSearchResponse; +import com.devonfw.cobigen.api.util.to.maven.MavenSearchResponse; +import com.devonfw.cobigen.api.util.to.nexus2.Nexus2SearchResponse; +import com.devonfw.cobigen.api.util.to.nexus3.Nexus3SearchResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Lists; + +/** + * Factory to create new instances of {@link AbstractSearchResponse} which handles the responses from various search + * REST APIs. + */ +public class SearchResponseFactory { + + /** Logger instance. */ + static final Logger LOG = LoggerFactory.getLogger(SearchResponseFactory.class); + + /** + * List of available {@link AbstractSearchResponse} implementations (add new search REST API responses here) + */ + private static final List SEARCH_RESPONSES = Lists.newArrayList(new MavenSearchResponse(), + new JfrogSearchResponse(), new Nexus2SearchResponse(), new Nexus3SearchResponse()); + + /** + * Searches for the maven artifact download links by given base URL, groupId and optional authentication token + * + * @param baseURL String of the repository server URL + * @param groupId the groupId to search for + * @param authToken bearer token to use for authentication + * @return List of download URLs + * @throws RestSearchResponseException if an error occurred while accessing the server + * @throws JsonProcessingException if the json processing was not possible + * @throws JsonMappingException if the json mapping was not possible + * @throws MalformedURLException if an URL was malformed + * + */ + public static List searchArtifactDownloadLinks(String baseURL, String groupId, String authToken) + throws RestSearchResponseException, JsonMappingException, JsonProcessingException, MalformedURLException { + + ObjectMapper mapper = new ObjectMapper(); + List downloadLinks = null; + + for (Object searchResponse : SEARCH_RESPONSES) { + try { + LOG.debug("Trying to get a response from {} with server URL: {} ...", + ((AbstractSearchResponse) searchResponse).getRepositoryType(), baseURL); + String jsonResponse = ((AbstractSearchResponse) searchResponse).retrieveJsonResponse(baseURL, groupId, + authToken); + AbstractSearchResponse response = (AbstractSearchResponse) mapper.readValue(jsonResponse, + searchResponse.getClass()); + return response.retrieveDownloadURLs(); + } catch (RestSearchResponseException e) { + LOG.error("It was not possible to get a response from {} using the URL: {}.\n Following error occured:\n {}", + ((AbstractSearchResponse) searchResponse).getRepositoryType(), baseURL, e.getMessage()); + } + } + + return downloadLinks; + } + +} \ No newline at end of file diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/jfrog/JfrogSearchResponse.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/jfrog/JfrogSearchResponse.java new file mode 100644 index 0000000000..19b8f646c3 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/jfrog/JfrogSearchResponse.java @@ -0,0 +1,69 @@ +package com.devonfw.cobigen.api.util.to.jfrog; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryConstants; +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryType; +import com.devonfw.cobigen.api.exception.RestSearchResponseException; +import com.devonfw.cobigen.api.util.to.AbstractSearchResponse; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Json model for jfrog Search REST API response + * + */ +public class JfrogSearchResponse extends AbstractSearchResponse { + + /** Logger instance. */ + @JsonIgnore + private static final Logger LOG = LoggerFactory.getLogger(JfrogSearchResponse.class); + + /** results */ + @JsonProperty("results") + private List results; + + /** + * @return results + */ + @JsonIgnore + public List getResults() { + + return this.results; + } + + @Override + @JsonIgnore + public String retrieveJsonResponse(String repositoryUrl, String groupId, String authToken) + throws RestSearchResponseException { + + String targetLink = repositoryUrl + "/" + MavenSearchRepositoryConstants.JFROG_TARGET_LINK + "?g=" + groupId; + + return retrieveJsonResponseWithAuthenticationToken(targetLink, authToken, getRepositoryType()); + } + + @Override + @JsonIgnore + public List retrieveDownloadURLs() throws MalformedURLException { + + List downloadLinks = new ArrayList<>(); + + for (JfrogSearchResponseResult result : getResults()) { + downloadLinks.add(new URL(result.getUri())); + } + + return downloadLinks; + } + + @Override + public MavenSearchRepositoryType getRepositoryType() { + + return MavenSearchRepositoryType.JFROG; + } +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/jfrog/JfrogSearchResponseResult.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/jfrog/JfrogSearchResponseResult.java new file mode 100644 index 0000000000..c880e12a66 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/jfrog/JfrogSearchResponseResult.java @@ -0,0 +1,26 @@ +package com.devonfw.cobigen.api.util.to.jfrog; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * Jfrog search response result model + * + */ +class JfrogSearchResponseResult { + + /** uri */ + @JsonProperty("uri") + private String uri; + + /** + * @return uri + */ + @JsonIgnore + public String getUri() { + + return this.uri; + } + +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponse.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponse.java new file mode 100644 index 0000000000..d320b23a21 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponse.java @@ -0,0 +1,79 @@ +package com.devonfw.cobigen.api.util.to.maven; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryConstants; +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryType; +import com.devonfw.cobigen.api.exception.RestSearchResponseException; +import com.devonfw.cobigen.api.util.to.AbstractSearchResponse; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Json model for maven Search REST API response + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class MavenSearchResponse extends AbstractSearchResponse { + + /** Logger instance. */ + @JsonIgnore + private static final Logger LOG = LoggerFactory.getLogger(MavenSearchResponse.class); + + @JsonProperty("response") + private MavenSearchResponseResponse response; + + /** + * @return response + */ + @JsonIgnore + public MavenSearchResponseResponse getResponse() { + + return this.response; + } + + @Override + @JsonIgnore + public String retrieveJsonResponse(String repositoryUrl, String groupId, String authToken) + throws RestSearchResponseException { + + String targetLink = repositoryUrl + "/" + MavenSearchRepositoryConstants.MAVEN_TARGET_LINK + "?q=g:" + groupId + + "&wt=json"; + + return retrieveJsonResponseWithAuthenticationToken(targetLink, authToken, getRepositoryType()); + } + + @Override + @JsonIgnore + public List retrieveDownloadURLs() throws MalformedURLException { + + List downloadLinks = new ArrayList<>(); + List docs = getResponse().getDocs(); + + for (MavenSearchResponseDoc doc : docs) { + for (String fileEnding : doc.getEc()) { + String newFileEnding = fileEnding; + downloadLinks.add( + AbstractSearchResponse.createDownloadLink(MavenSearchRepositoryConstants.MAVEN_REPOSITORY_DOWNLOAD_LINK, + doc.getGroup(), doc.getArtifact(), doc.getLatestVersion(), newFileEnding)); + } + + } + + return downloadLinks; + } + + @Override + public MavenSearchRepositoryType getRepositoryType() { + + return MavenSearchRepositoryType.MAVEN; + } + +} \ No newline at end of file diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponseDoc.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponseDoc.java new file mode 100644 index 0000000000..8d5d7b2eb4 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponseDoc.java @@ -0,0 +1,95 @@ +package com.devonfw.cobigen.api.util.to.maven; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * Maven search response doc model + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +class MavenSearchResponseDoc { + + /** id */ + @JsonProperty("id") + private String id; + + /** group */ + @JsonProperty("g") + private String group; + + /** artifact */ + @JsonProperty("a") + private String artifact; + + /** latest version */ + @JsonProperty("latestVersion") + private String latestVersion; + + /** repository ID */ + @JsonProperty("repositoryId") + private String repositoryId; + + /** ec (file ending) */ + @JsonProperty("ec") + private List ec; + + /** + * @return ec + */ + @JsonIgnore + public List getEc() { + + return this.ec; + } + + /** + * @return id + */ + @JsonIgnore + public String getId() { + + return this.id; + } + + /** + * @return group + */ + @JsonIgnore + public String getGroup() { + + return this.group; + } + + /** + * @return artifact + */ + @JsonIgnore + public String getArtifact() { + + return this.artifact; + } + + /** + * @return latestVersion + */ + @JsonIgnore + public String getLatestVersion() { + + return this.latestVersion; + } + + /** + * @return repositoryId + */ + @JsonIgnore + public String getRepositoryId() { + + return this.repositoryId; + } + +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponseResponse.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponseResponse.java new file mode 100644 index 0000000000..ec30cb7487 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponseResponse.java @@ -0,0 +1,45 @@ +package com.devonfw.cobigen.api.util.to.maven; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * Maven search response model + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +class MavenSearchResponseResponse { + + /** + * found results + */ + @JsonProperty("numFound") + private int numFound; + + /** docs */ + @JsonProperty("docs") + private List docs; + + /** + * @return numFound + */ + @JsonIgnore + public int getNumFound() { + + return this.numFound; + } + + /** + * @return docs + */ + @JsonIgnore + public List getDocs() { + + return this.docs; + } + +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponeArtifactLinks.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponeArtifactLinks.java new file mode 100644 index 0000000000..9016e1a95f --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponeArtifactLinks.java @@ -0,0 +1,27 @@ +package com.devonfw.cobigen.api.util.to.nexus2; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * Nexus 2 search response artifact links model + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +class Nexus2SearchResponeArtifactLinks { + + @JsonProperty("extension") + private String extension; + + /** + * @return extension + */ + public String getExtension() { + + return this.extension; + } + + @JsonProperty("classifier") + private String classifier; +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponse.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponse.java new file mode 100644 index 0000000000..3bd9ada1ca --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponse.java @@ -0,0 +1,70 @@ +package com.devonfw.cobigen.api.util.to.nexus2; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryConstants; +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryType; +import com.devonfw.cobigen.api.exception.RestSearchResponseException; +import com.devonfw.cobigen.api.util.to.AbstractSearchResponse; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Json model for nexus2 Search REST API response + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Nexus2SearchResponse extends AbstractSearchResponse { + + /** Logger instance. */ + @JsonIgnore + private static final Logger LOG = LoggerFactory.getLogger(Nexus2SearchResponse.class); + + /** data */ + @JsonProperty("data") + private List data; + + @Override + @JsonIgnore + public List retrieveDownloadURLs() throws MalformedURLException { + + List downloadLinks = new ArrayList<>(); + + for (Nexus2SearchResponseData item : this.data) { + for (Nexus2SearchResponseArtifactHits artifactHit : item.artifactHits) { + for (Nexus2SearchResponeArtifactLinks artifactLink : artifactHit.artifactLinks) { + downloadLinks.add(AbstractSearchResponse.createDownloadLink( + MavenSearchRepositoryConstants.NEXUS2_REPOSITORY_URL + "/" + + MavenSearchRepositoryConstants.NEXUS2_REPOSITORY_LINK, + item.getGroupId(), item.getArtifactId(), item.getVersion(), "." + artifactLink.getExtension())); + + } + } + } + + return removeDuplicatedDownloadURLs(downloadLinks); + } + + @Override + @JsonIgnore + public String retrieveJsonResponse(String repositoryUrl, String groupId, String authToken) + throws RestSearchResponseException { + + String targetLink = repositoryUrl + "/" + MavenSearchRepositoryConstants.NEXUS2_TARGET_LINK + "?q=" + groupId; + + return retrieveJsonResponseWithAuthenticationToken(targetLink, authToken, getRepositoryType()); + } + + @Override + public MavenSearchRepositoryType getRepositoryType() { + + return MavenSearchRepositoryType.NEXUS2; + } +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponseArtifactHits.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponseArtifactHits.java new file mode 100644 index 0000000000..54fa1a5190 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponseArtifactHits.java @@ -0,0 +1,20 @@ +package com.devonfw.cobigen.api.util.to.nexus2; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * Nexus search response artifacthits model + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +class Nexus2SearchResponseArtifactHits { + + /** artifactLinks */ + @JsonProperty("artifactLinks") + public List artifactLinks; + +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponseData.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponseData.java new file mode 100644 index 0000000000..b87dc24c17 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponseData.java @@ -0,0 +1,56 @@ +package com.devonfw.cobigen.api.util.to.nexus2; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * Nexus2 search response data model + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +class Nexus2SearchResponseData { + + /** groupId */ + @JsonProperty("groupId") + private String groupId; + + /** artifactId */ + @JsonProperty("artifactId") + private String artifactId; + + /** version */ + @JsonProperty("version") + private String version; + + /** artifactHits */ + @JsonProperty("artifactHits") + public List artifactHits; + + /** + * @return groupId + */ + public String getGroupId() { + + return this.groupId; + } + + /** + * @return artifactId + */ + public String getArtifactId() { + + return this.artifactId; + } + + /** + * @return version + */ + public String getVersion() { + + return this.version; + } + +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponse.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponse.java new file mode 100644 index 0000000000..4a31f6eca8 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponse.java @@ -0,0 +1,65 @@ +package com.devonfw.cobigen.api.util.to.nexus3; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryConstants; +import com.devonfw.cobigen.api.constants.MavenSearchRepositoryType; +import com.devonfw.cobigen.api.exception.RestSearchResponseException; +import com.devonfw.cobigen.api.util.to.AbstractSearchResponse; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Json model for nexus3 Search REST API response + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Nexus3SearchResponse extends AbstractSearchResponse { + + /** Logger instance. */ + @JsonIgnore + private static final Logger LOG = LoggerFactory.getLogger(Nexus3SearchResponse.class); + + /** items */ + @JsonProperty("items") + private List items; + + @Override + @JsonIgnore + public List retrieveDownloadURLs() throws MalformedURLException { + + List downloadLinks = new ArrayList<>(); + + for (Nexus3SearchResponseItem item : this.items) { + for (Nexus3SearchResponseAsset asset : item.assets) { + downloadLinks.add(new URL(asset.downloadUrl)); + } + } + + return removeDuplicatedDownloadURLs(downloadLinks); + } + + @Override + @JsonIgnore + public String retrieveJsonResponse(String repositoryUrl, String groupId, String authToken) + throws RestSearchResponseException { + + String targetLink = repositoryUrl + "/" + MavenSearchRepositoryConstants.NEXUS3_TARGET_LINK + + "?repository=maven-central" + "&group=" + groupId; + + return retrieveJsonResponseWithAuthenticationToken(targetLink, authToken, getRepositoryType()); + } + + @Override + public MavenSearchRepositoryType getRepositoryType() { + + return MavenSearchRepositoryType.NEXUS3; + } +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponseAsset.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponseAsset.java new file mode 100644 index 0000000000..7ed9689f79 --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponseAsset.java @@ -0,0 +1,18 @@ +package com.devonfw.cobigen.api.util.to.nexus3; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * Nexus3 search response asset model + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +class Nexus3SearchResponseAsset { + + /** downloadUrl */ + @JsonProperty("downloadUrl") + public String downloadUrl; + +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponseItem.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponseItem.java new file mode 100644 index 0000000000..6fab4b864c --- /dev/null +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponseItem.java @@ -0,0 +1,20 @@ +package com.devonfw.cobigen.api.util.to.nexus3; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * Nexus3 search response item model + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +class Nexus3SearchResponseItem { + + /** artifactHits */ + @JsonProperty("assets") + public List assets; + +} diff --git a/cobigen/cobigen-core-api/src/test/java/com/devonfw/cobigen/api/MavenUtilTest.java b/cobigen/cobigen-core-api/src/test/java/com/devonfw/cobigen/api/MavenUtilTest.java new file mode 100644 index 0000000000..9674c14c13 --- /dev/null +++ b/cobigen/cobigen-core-api/src/test/java/com/devonfw/cobigen/api/MavenUtilTest.java @@ -0,0 +1,330 @@ +package com.devonfw.cobigen.api; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import org.junit.Rule; +import org.junit.Test; + +import com.devonfw.cobigen.api.exception.RestSearchResponseException; +import com.devonfw.cobigen.api.util.MavenUtil; +import com.devonfw.cobigen.api.util.to.AbstractSearchResponse; +import com.devonfw.cobigen.api.util.to.jfrog.JfrogSearchResponse; +import com.devonfw.cobigen.api.util.to.maven.MavenSearchResponse; +import com.devonfw.cobigen.api.util.to.nexus2.Nexus2SearchResponse; +import com.devonfw.cobigen.api.util.to.nexus3.Nexus3SearchResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +/** + * Test class for maven utilities + */ +public class MavenUtilTest { + + /** + * WireMock rule to initialize + */ + @Rule + public WireMockRule wireMockRule = new WireMockRule(options().disableRequestJournal()); + + /** Testdata root path */ + private static final String testdataRoot = "src/test/resources/testdata/unittest/MavenUtilTest"; + + /** + * Tests if retrieving maven artifacts with an invalid link returns null + */ + public void testRetrieveMavenArtifactsWithInvalidLinkReturnsNull() { + + assertThat(MavenUtil.retrieveMavenArtifactsByGroupId("this/is/not/a/link", "test", null)).isNull(); + } + + /** + * Tests if a {@link RestSearchResponseException} gets thrown when a faulty target link without a token was used + */ + @Test + public void testWrongTargetLinkThrowsException() { + + try { + AbstractSearchResponse.retrieveJsonResponseWithAuthenticationToken("this/is/not/a/link", null, null); + } catch (RestSearchResponseException e) { + assertThat(e).hasMessage("The target URL was faulty."); + } + } + + /** + * Tests if an exception gets thrown when a faulty target link and token was used + */ + @Test + public void testWrongTargetLinkAndTokenThrowsException() { + + try { + AbstractSearchResponse.retrieveJsonResponseWithAuthenticationToken("this/is/not/a/link", "thisisabadtoken", null); + } catch (RestSearchResponseException e) { + assertThat(e).hasMessage("The target URL was faulty."); + } + } + + /** + * Tests if a {@link RestSearchResponseException} gets thrown when a status code was not 200 but 400 instead + */ + @Test + public void testWrongResponseStatusCodeThrowsException() { + + try { + AbstractSearchResponse + .retrieveJsonResponseWithAuthenticationToken("https://search.maven.org/solrsearch/select?test", null, null); + } catch (RestSearchResponseException e) { + assertThat(e).hasMessage("The search REST API returned the unexpected status code: 400"); + } + } + + /** + * Tests if maven json response can properly be parsed and converted to a list of download URLs + * + * @throws IOException if an error occurred while reading the test json file + */ + @Test + public void testMavenParseDownloadLinks() throws IOException { + + // given + ObjectMapper mapper = new ObjectMapper(); + MavenSearchResponse response = new MavenSearchResponse(); + + String jsonResponse = new String(Files.readAllBytes(Paths.get(testdataRoot).resolve("mavenJsonTest.json"))); + + response = mapper.readValue(jsonResponse, MavenSearchResponse.class); + // when + List downloadLinks = response.retrieveDownloadURLs(); + + // then + assertThat(downloadLinks).contains( + new URL("https://repo1.maven.org/maven2/com/google/inject/guice/5.1.0/guice-5.1.0.jar"), + new URL("https://repo1.maven.org/maven2/com/google/inject/guice-bom/5.1.0/guice-bom-5.1.0.pom"), + new URL("https://repo1.maven.org/maven2/com/google/inject/guice-parent/5.1.0/guice-parent-5.1.0.pom"), + new URL("https://repo1.maven.org/maven2/com/google/inject/jdk8-tests/5.0.1/jdk8-tests-5.0.1.jar")); + } + + /** + * Tests if nexus2 json response can properly be parsed and converted to a list of download URLs + * + * @throws IOException if an error occurred while reading the test json file + */ + @Test + public void testNexus2ParseDownloadLinks() throws IOException { + + // given + ObjectMapper mapper = new ObjectMapper(); + Nexus2SearchResponse response = new Nexus2SearchResponse(); + + String jsonResponse = new String(Files.readAllBytes(Paths.get(testdataRoot).resolve("nexus2JsonTest.json"))); + + response = mapper.readValue(jsonResponse, Nexus2SearchResponse.class); + + // when + List downloadLinks = response.retrieveDownloadURLs(); + + // then + assertThat(downloadLinks).contains(new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/openapiplugin/2021.12.006/openapiplugin-2021.12.006.pom"), + new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/openapiplugin/2021.12.006/openapiplugin-2021.12.006.jar"), + new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/openapiplugin/2021.12.005/openapiplugin-2021.12.005.pom"), + new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/openapiplugin/2021.12.005/openapiplugin-2021.12.005.jar"), + new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/jsonplugin/2021.12.006/jsonplugin-2021.12.006.pom"), + new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/jsonplugin/2021.12.006/jsonplugin-2021.12.006.jar"), + new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/jsonplugin/2021.12.005/jsonplugin-2021.12.005.pom"), + new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/jsonplugin/2021.12.005/jsonplugin-2021.12.005.jar")); + } + + /** + * Tests if nexus3 json response can properly be parsed and converted to a list of download URLs + * + * @throws IOException if an error occurred while reading the test json file + */ + @Test + public void testNexus3ParseDownloadLinks() throws IOException { + + // given + ObjectMapper mapper = new ObjectMapper(); + Nexus3SearchResponse response = new Nexus3SearchResponse(); + + String jsonResponse = new String(Files.readAllBytes(Paths.get(testdataRoot).resolve("nexus3JsonTest.json"))); + + response = mapper.readValue(jsonResponse, Nexus3SearchResponse.class); + + // when + List downloadLinks = response.retrieveDownloadURLs(); + + // then + assertThat(downloadLinks).contains(new URL( + "http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1-sources.jar"), + new URL("http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1.jar"), + new URL("http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1.pom")); + } + + /** + * Tests if jfrog json response can properly be parsed and converted to a list of download URLs + * + * @throws IOException if an error occurred while reading the test json file + */ + @Test + public void testJfrogParseDownloadLinks() throws IOException { + + // given + ObjectMapper mapper = new ObjectMapper(); + JfrogSearchResponse response = new JfrogSearchResponse(); + + String jsonResponse = new String(Files.readAllBytes(Paths.get(testdataRoot).resolve("jfrogJsonTest.json"))); + + // when + response = mapper.readValue(jsonResponse, JfrogSearchResponse.class); + List downloadLinks = response.retrieveDownloadURLs(); + + // then + assertThat(downloadLinks).contains(new URL( + "http://localhost:8081/artifactory/api/storage/libs-release-local/org/acme/artifact/1.0/artifact-1.0-sources.jar"), + new URL( + "http://localhost:8081/artifactory/api/storage/libs-release-local/org/acme/artifactB/1.0/artifactB-1.0-sources.jar")); + } + + /** + * Tests if a request to maven search REST API returns a list of download URLs + * + * @throws IOException if an error occurred while reading the test json file + */ + @Test + public void testMavenSearchRequestGetsValidDownloadLinks() throws IOException { + + // given + List downloadList; + + this.wireMockRule.stubFor(get(urlMatching("/solrsearch/select.*")).willReturn(aResponse().withStatus(200) + .withBody(Files.readAllBytes(Paths.get(testdataRoot).resolve("mavenJsonTest.json"))))); + + this.wireMockRule + .stubFor(get(urlMatching("/artifactory/api/search/gavc.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule + .stubFor(get(urlMatching("/service/local/lucene/search/.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule.stubFor(get(urlMatching("/service/rest/v1/search.*")).willReturn(aResponse().withStatus(404))); + + // when + downloadList = MavenUtil.retrieveMavenArtifactsByGroupId("http://localhost:8080", "com.google.inject", null); + + // then + assertThat(downloadList).contains( + new URL("https://repo1.maven.org/maven2/com/google/inject/guice/5.1.0/guice-5.1.0.jar"), + new URL("https://repo1.maven.org/maven2/com/google/inject/guice-bom/5.1.0/guice-bom-5.1.0.pom"), + new URL("https://repo1.maven.org/maven2/com/google/inject/guice-parent/5.1.0/guice-parent-5.1.0.pom"), + new URL("https://repo1.maven.org/maven2/com/google/inject/jdk8-tests/5.0.1/jdk8-tests-5.0.1.jar")); + } + + /** + * Tests if a request to nexus2 search REST API returns a list of download URLs + * + * @throws IOException if an error occurred while reading the test json file + */ + @Test + public void testNexus2SearchRequestGetsValidDownloadLinks() throws IOException { + + // given + List downloadList; + + this.wireMockRule.stubFor(get(urlMatching("/artifactory/solrsearch.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule + .stubFor(get(urlMatching("/artifactory/api/search/gavc.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule.stubFor(get(urlMatching("/service/local/lucene/search.*")).willReturn(aResponse().withStatus(200) + .withBody(Files.readAllBytes(Paths.get(testdataRoot).resolve("nexus2JsonTest.json"))))); + + this.wireMockRule.stubFor(get(urlMatching("/service/rest/v1/search.*")).willReturn(aResponse().withStatus(404))); + + // when + downloadList = MavenUtil.retrieveMavenArtifactsByGroupId("http://localhost:8080", "com.devonfw.cobigen", null); + + // then + assertThat(downloadList).contains(new URL( + "https://s01.oss.sonatype.org/service/local/repositories/releases/content/com/devonfw/cobigen/openapiplugin/2021.12.006/openapiplugin-2021.12.006.jar")); + } + + /** + * Tests if a request to nexus3 search REST API returns a list of download URLs + * + * @throws IOException if an error occurred while reading the test json file + */ + @Test + public void testNexus3SearchRequestGetsValidDownloadLinks() throws IOException { + + // given + List downloadList; + + this.wireMockRule.stubFor(get(urlMatching("/artifactory/solrsearch/.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule + .stubFor(get(urlMatching("/artifactory/api/search/gavc.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule + .stubFor(get(urlMatching("/service/local/lucene/search/.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule.stubFor(get(urlMatching("/service/rest/v1/search.*")).willReturn(aResponse().withStatus(200) + .withBody(Files.readAllBytes(Paths.get(testdataRoot).resolve("nexus3JsonTest.json"))))); + + // when + downloadList = MavenUtil.retrieveMavenArtifactsByGroupId("http://localhost:8080", "com.devonfw.cobigen", null); + + // then + assertThat(downloadList).contains(new URL( + "http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1-sources.jar"), + new URL("http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1.jar"), + new URL("http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1.pom")); + } + + /** + * Tests if a request to jfrog search REST API returns a list of download URLs + * + * @throws IOException if an error occurred while reading the test json file + */ + @Test + public void testJfrogSearchRequestGetsValidDownloadLinks() throws IOException { + + // given + List downloadList; + + this.wireMockRule.stubFor(get(urlMatching("/artifactory/solrsearch/.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule.stubFor(get(urlMatching("/artifactory/api/search/gavc.*")).willReturn(aResponse().withStatus(200) + .withBody(Files.readAllBytes(Paths.get(testdataRoot).resolve("jfrogJsonTest.json"))))); + + this.wireMockRule + .stubFor(get(urlMatching("/service/local/lucene/search/.*")).willReturn(aResponse().withStatus(404))); + + this.wireMockRule.stubFor(get(urlMatching("/service/rest/v1/search.*")).willReturn(aResponse().withStatus(404))); + + // when + downloadList = MavenUtil.retrieveMavenArtifactsByGroupId("http://localhost:8080", "com.devonfw.cobigen", null); + + // then + assertThat(downloadList).contains(new URL( + "http://localhost:8081/artifactory/api/storage/libs-release-local/org/acme/artifact/1.0/artifact-1.0-sources.jar"), + new URL( + "http://localhost:8081/artifactory/api/storage/libs-release-local/org/acme/artifactB/1.0/artifactB-1.0-sources.jar")); + } + +} diff --git a/cobigen/cobigen-core-api/src/test/resources/logback-test.xml b/cobigen/cobigen-core-api/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..7a043e0457 --- /dev/null +++ b/cobigen/cobigen-core-api/src/test/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] [C:%X{correlationId}] - %-5level - %logger{36} - %msg%n + + + + + + + + + + + \ No newline at end of file diff --git a/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/jfrogJsonTest.json b/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/jfrogJsonTest.json new file mode 100644 index 0000000000..d208ba0267 --- /dev/null +++ b/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/jfrogJsonTest.json @@ -0,0 +1,9 @@ +{ +"results": [ + { + "uri": "http://localhost:8081/artifactory/api/storage/libs-release-local/org/acme/artifact/1.0/artifact-1.0-sources.jar" + },{ + "uri": "http://localhost:8081/artifactory/api/storage/libs-release-local/org/acme/artifactB/1.0/artifactB-1.0-sources.jar" + } +] +} \ No newline at end of file diff --git a/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/mavenJsonTest.json b/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/mavenJsonTest.json new file mode 100644 index 0000000000..6401fe8466 --- /dev/null +++ b/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/mavenJsonTest.json @@ -0,0 +1,118 @@ +{ + "responseHeader":{ + "status":0, + "QTime":1, + "params":{ + "q":"g:com.google.inject", + "core":"", + "indent":"off", + "spellcheck":"true", + "fl":"id,g,a,latestVersion,p,ec,repositoryId,text,timestamp,versionCount", + "start":"", + "sort":"score desc,timestamp desc,g asc,a asc", + "spellcheck.count":"5", + "rows":"20", + "wt":"json", + "version":"2.2" + } + }, + "response":{ + "numFound":4, + "start":0, + "docs":[ + { + "id":"com.google.inject:guice", + "g":"com.google.inject", + "a":"guice", + "latestVersion":"5.1.0", + "repositoryId":"central", + "p":"jar", + "timestamp":1643061977000, + "versionCount":19, + "text":[ + "com.google.inject", + "guice", + "-javadoc.jar", + "-sources.jar", + "-test-sources.jar", + "-tests.jar", + ".jar", + "-classes.jar", + ".pom" + ], + "ec":[ + "-javadoc.jar", + "-sources.jar", + "-test-sources.jar", + "-tests.jar", + ".jar", + "-classes.jar", + ".pom" + ] + }, + { + "id":"com.google.inject:guice-bom", + "g":"com.google.inject", + "a":"guice-bom", + "latestVersion":"5.1.0", + "repositoryId":"central", + "p":"pom", + "timestamp":1643061926000, + "versionCount":10, + "text":[ + "com.google.inject", + "guice-bom", + ".pom" + ], + "ec":[ + ".pom" + ] + }, + { + "id":"com.google.inject:guice-parent", + "g":"com.google.inject", + "a":"guice-parent", + "latestVersion":"5.1.0", + "repositoryId":"central", + "p":"pom", + "timestamp":1643061923000, + "versionCount":19, + "text":[ + "com.google.inject", + "guice-parent", + ".pom" + ], + "ec":[ + ".pom" + ] + }, + { + "id":"com.google.inject:jdk8-tests", + "g":"com.google.inject", + "a":"jdk8-tests", + "latestVersion":"5.0.1", + "repositoryId":"central", + "p":"jar", + "timestamp":1614380739000, + "versionCount":7, + "text":[ + "com.google.inject", + "jdk8-tests", + ".jar", + "-tests.jar", + ".pom" + ], + "ec":[ + ".jar", + "-tests.jar", + ".pom" + ] + } + ] + }, + "spellcheck":{ + "suggestions":[ + + ] + } +} \ No newline at end of file diff --git a/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/nexus2JsonTest.json b/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/nexus2JsonTest.json new file mode 100644 index 0000000000..73b88a9376 --- /dev/null +++ b/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/nexus2JsonTest.json @@ -0,0 +1,135 @@ +{ + "totalCount":305, + "from":-1, + "count":-1, + "tooManyResults":false, + "collapsed":false, + "repoDetails":[ + { + "repositoryId":"releases", + "repositoryName":"Releases", + "repositoryContentClass":"maven2", + "repositoryKind":"hosted", + "repositoryPolicy":"RELEASE", + "repositoryURL":"https://s01.oss.sonatype.org/service/local/repositories/releases" + } + ], + "data":[ + { + "groupId":"com.devonfw.cobigen", + "artifactId":"openapiplugin", + "version":"2021.12.006", + "latestRelease":"2021.12.006", + "latestReleaseRepositoryId":"releases", + "highlightedFragment":"
Group ID
  • com<\/B>.devonfw<\/B>.cobigen<\/B><\/LI><\/UL><\/blockquote>", + "artifactHits":[ + { + "repositoryId":"releases", + "artifactLinks":[ + { + "extension":"pom" + }, + { + "extension":"jar" + }, + { + "classifier":"javadoc", + "extension":"jar" + }, + { + "classifier":"sources", + "extension":"jar" + } + ] + } + ] + }, + { + "groupId":"com.devonfw.cobigen", + "artifactId":"openapiplugin", + "version":"2021.12.005", + "latestRelease":"2021.12.006", + "latestReleaseRepositoryId":"releases", + "highlightedFragment":"
    Group ID
    • com<\/B>.devonfw<\/B>.cobigen<\/B><\/LI><\/UL><\/blockquote>", + "artifactHits":[ + { + "repositoryId":"releases", + "artifactLinks":[ + { + "extension":"pom" + }, + { + "extension":"jar" + }, + { + "classifier":"javadoc", + "extension":"jar" + }, + { + "classifier":"sources", + "extension":"jar" + } + ] + } + ] + }, + { + "groupId":"com.devonfw.cobigen", + "artifactId":"jsonplugin", + "version":"2021.12.006", + "latestRelease":"2021.12.006", + "latestReleaseRepositoryId":"releases", + "highlightedFragment":"
      Group ID
      • com<\/B>.devonfw<\/B>.cobigen<\/B><\/LI><\/UL><\/blockquote>", + "artifactHits":[ + { + "repositoryId":"releases", + "artifactLinks":[ + { + "extension":"pom" + }, + { + "extension":"jar" + }, + { + "classifier":"javadoc", + "extension":"jar" + }, + { + "classifier":"sources", + "extension":"jar" + } + ] + } + ] + }, + { + "groupId":"com.devonfw.cobigen", + "artifactId":"jsonplugin", + "version":"2021.12.005", + "latestRelease":"2021.12.006", + "latestReleaseRepositoryId":"releases", + "highlightedFragment":"
        Group ID
        • com<\/B>.devonfw<\/B>.cobigen<\/B><\/LI><\/UL><\/blockquote>", + "artifactHits":[ + { + "repositoryId":"releases", + "artifactLinks":[ + { + "extension":"pom" + }, + { + "classifier":"javadoc", + "extension":"jar" + }, + { + "extension":"jar" + }, + { + "classifier":"sources", + "extension":"jar" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/nexus3JsonTest.json b/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/nexus3JsonTest.json new file mode 100644 index 0000000000..4488061d96 --- /dev/null +++ b/cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/nexus3JsonTest.json @@ -0,0 +1,42 @@ +{ + "items" : [ { + "id" : "bWF2ZW4tY2VudHJhbDoyZTQ3ZGRhMGYxYjU1NWUwNzE1OWRjOWY5ZGQzZmVmNA", + "repository" : "maven-central", + "format" : "maven2", + "group" : "org.osgi", + "name" : "org.osgi.core", + "version" : "4.3.1", + "assets" : [ { + "downloadUrl" : "http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1-sources.jar", + "path" : "org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1-sources.jar", + "id" : "bWF2ZW4tY2VudHJhbDplMDE4OGVkMDcyOGZhNjhmNDExNzU2OGU1MjQ2NjZiYg", + "repository" : "maven-central", + "format" : "maven2", + "checksum" : { + "sha1" : "80bfafcf783988442b3a58318face1d2132db33d", + "md5" : "87ee0258b79dc852626b91818316b9c3" + } + }, { + "downloadUrl" : "http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1.jar", + "path" : "org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1.jar", + "id" : "bWF2ZW4tY2VudHJhbDpkMDY0ODA0YThlZDVhZDZlNjhmZGU5MWNmM2NiZTgzMw", + "repository" : "maven-central", + "format" : "maven2", + "checksum" : { + "sha1" : "5458ffe2ba049e76c29f2df2dc3ffccddf8b839e", + "md5" : "8053bbc1b55d51f5abae005625209d08" + } + }, { + "downloadUrl" : "http://localhost:8081/repository/maven-central/org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1.pom", + "path" : "org/osgi/org.osgi.core/4.3.1/org.osgi.core-4.3.1.pom", + "id" : "bWF2ZW4tY2VudHJhbDo2NTRiYjdkMGE1OTIxMzg1OWZhMTVkMzNmYWU1ZmY3OA", + "repository" : "maven-central", + "format" : "maven2", + "checksum" : { + "sha1" : "79391fc69dd72ad1fd983d01b4572f93f644882b", + "md5" : "3d87a59bcdb4b131d9a63e87e0ed924a" + } + } ] + } ], + "continuationToken" : null +} \ No newline at end of file diff --git a/cobigen/core-externalprocess-api/pom.xml b/cobigen/core-externalprocess-api/pom.xml index 1e1c2958d6..392ee5c6a6 100644 --- a/cobigen/core-externalprocess-api/pom.xml +++ b/cobigen/core-externalprocess-api/pom.xml @@ -25,7 +25,10 @@ com.squareup.okhttp3 okhttp - 4.9.1 + + + com.squareup.okio + okio com.google.code.gson diff --git a/pom.xml b/pom.xml index d3f74992d9..e1fcaac515 100644 --- a/pom.xml +++ b/pom.xml @@ -127,13 +127,23 @@ com.fasterxml.jackson.core jackson-databind - 2.13.2.2 + 2.13.3 com.google.code.gson gson 2.8.6 + + com.squareup.okhttp3 + okhttp + 4.10.0 + + + com.squareup.okio + okio + 3.2.0 + jaxen jaxen