From f20d73106309bab9d4b453ee58d89177c7991657 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com> Date: Wed, 14 Sep 2022 22:43:36 +0200 Subject: [PATCH] #1529 added first implementation of extensible artifact search REST API (#1556) * #1529 added first implementation of extensible artifact search REST API added new getMavenArtifactsByGroupId to MavenUtils which takes a repository type (e.g. maven, nexus, jfrog) and a groupId to search for added new SearchResponse tos to core-api utils added new MavenUtilTest class (tests for proper json string parsing and REST API requests) and resources * #1529 added missing dependencies * #1529 implemented requested changes added new RESTSearchResponseException replaced IOException with RESTSearchResponseException * #1529 fixed jFrog API * #1529 implemented requested changes changed repositoryType from String to MavenSearchRepositoryType enum added new MavenSearchRepositoryType enum * #1529 implemented requested changes replaced fixed repository URLs with constants added new MavenSearchRepositoryConstants * #1529 implemented requested changes converted MavenSearchResponseConstants to uppercase added MAVEN, NEXUS and JFROG TARGET_LINK constants added MAVEN_MAX_RESPONSE_ROWS constant added javadoc to all models (moved main model to top) renamed json response models * #1529 adjusted nexus REST API adjusted nexus REST API to v2.0 adjusted nexus REST API tests and resources to v2.0 refactored createDownloadLink method (moved to AbstractRESTSearchResponse) added new constants for MAVEN_REPOSITORY_LINK, NEXUS_REPOSITORY_URL, NEXUS_REPOSITORY_URL, NEXUS_TARGET_LINK, NEXUS_DC_ID added ec param to MavenSearchResponse * #1529 disabled jfrog REST API test * #1529 added optimizations + authentication added bearer token authentication added bearer token to jfrog renamed nexus to nexus2 repository type added nexus3 repository type added more tests for code coverage * #1529 applied factory pattern added new SearchResponse interface made all SearchResponse types inherit from SearchResponse added new SearchResponseFactory refactored getArtifactDownloadLinks method added new getAvailableSearchInterfaces method (used to register new search interfaces) added getRepositoryType to SearchResponse parent class (returns the type of repository as an enum) * #1529 moved utility methods from factory class to util class added new SearchResponseUtil class * changed javadoc * #1529 implemented requested changes renamed RESTSearchResponse signature msg to message * #1529 implemented requested changes removed limitRows functionality removed NEXUS2_DC_ID from target link adjusted MavenSearchResponseConstants javadoc * #1529 implemented requested changes converted getAvailableSearchInterfaces method to final list of SearchResponses adjusted javadoc * #1529 added missing javadoc * #1529 implemented requested changes added more detailed javadoc * #1529 implemented requested changes converted RESTSearchResponseException name to PascalCase * #1529 implemented requested changes renamed getMavenArtifactsByGroupId to retrieveMavenArtifactsByGroupId * #1529 implemented requested changes renamed getArtifactDownloadLinks to searchArtifactDownloadLinks * #1529 implemented requested changes moved multi search response classes to separate packages * #1529 implemented requested changes improved javadoc readability * #1529 implemented requested changes converted SearchResponse to AbstractSearchResponse refactored getJsonResponse added new retrieveJsonResponseWithAuthentication method to AbstractSearchResponse renamed getJsonResponse to retrieveJsonResponse * #1529 implemented requested changes refactored getDownloadURLs method added new removeDuplicatedDownloadURLs method to AbstractSearchResponse renamed getDownloadURLs to retrieveDownloadURLs * #1529 implemented requested changes refactored SearchResponseUtil methods moved SearchResponseUtil methods to AbstractSearchResponse removed SearchResponseUtil class * #1529 implemented requested changes removed throw CobiGenRuntimeExceptions to ensure that an error with the API won't stop CobiGen execution replaced CobiGenRuntimeException in MavenUtil with error log and a return of null converted testWrongRepositoryTypeThrowsException to testRetrieveMavenArtifactsWithInvalidLinkReturnsNull replaced throw exception with error log message * #1529 implemented requested changes refactored getJsonResponseStringByTargetLink replaced getJsonResponseStringByTargetLink with retrieveJsonResponseWithAuthenticationToken added MavenSearchRepositoryType to retrieveJsonResponseWithAuthenticationToken * #1529 implemented requested changes replaced fixed ignored json properties with ignore unknown param * #1529 added WireMock to tests made ignored tests functional with WireMock added WireMock stubs for each case updated jersey from 3.0.5 to 3.0.7 added wiremock-standalone 2.27.2 (used older version because of conflicts with jackson) * #1529 reduced WireMock logging added logback-test.xml (sets WireMock to WARN log level) * #1529 updated jackson updated jackson-databind from 2.13.2.2 to 2.13.3 * #1529 fixed FileUtils issue replaced readString with readAllBytes * #1529 implemented requested changes renamed baseURL to baseUrl removed LOG concatenation * #1529 implemented requested changes removed ProcessingException from SearchResponseFactory * #1529 implemented requested changes added artifactory path to jfrog target link constant * #1529 removed jersey dependencies replaced jersey with OkHttpClient restricted MavenUtilTests (added check of messages) added jackson-databind to core-api dependencies added okhttp to core-api dependencies * #1529 updated okhttp changed okhttp version from 4.9.1 to 4.10.0 * #1529 added okhttp version to root pom added latest okhttp dependeny to root pom.xml removed fixed okhttp versions from core-api and core-externalprocess-api * #1529 added okio added transitive okio dependency * #1529 implemented requested changes Changed enum values to uppercase * #1529 implemented requested changes moved status into not null if condition Co-authored-by: EduardKrieger --- cobigen/cobigen-core-api/pom.xml | 23 +- .../MavenSearchRepositoryConstants.java | 58 +++ .../constants/MavenSearchRepositoryType.java | 28 ++ .../RestSearchResponseException.java | 40 +++ .../devonfw/cobigen/api/util/MavenUtil.java | 26 ++ .../api/util/to/AbstractSearchResponse.java | 164 +++++++++ .../api/util/to/SearchResponseFactory.java | 72 ++++ .../util/to/jfrog/JfrogSearchResponse.java | 69 ++++ .../to/jfrog/JfrogSearchResponseResult.java | 26 ++ .../util/to/maven/MavenSearchResponse.java | 79 +++++ .../util/to/maven/MavenSearchResponseDoc.java | 95 +++++ .../to/maven/MavenSearchResponseResponse.java | 45 +++ .../Nexus2SearchResponeArtifactLinks.java | 27 ++ .../util/to/nexus2/Nexus2SearchResponse.java | 70 ++++ .../Nexus2SearchResponseArtifactHits.java | 20 ++ .../to/nexus2/Nexus2SearchResponseData.java | 56 +++ .../util/to/nexus3/Nexus3SearchResponse.java | 65 ++++ .../to/nexus3/Nexus3SearchResponseAsset.java | 18 + .../to/nexus3/Nexus3SearchResponseItem.java | 20 ++ .../devonfw/cobigen/api/MavenUtilTest.java | 330 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 17 + .../unittest/MavenUtilTest/jfrogJsonTest.json | 9 + .../unittest/MavenUtilTest/mavenJsonTest.json | 118 +++++++ .../MavenUtilTest/nexus2JsonTest.json | 135 +++++++ .../MavenUtilTest/nexus3JsonTest.json | 42 +++ cobigen/core-externalprocess-api/pom.xml | 5 +- pom.xml | 12 +- 27 files changed, 1666 insertions(+), 3 deletions(-) create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/constants/MavenSearchRepositoryConstants.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/constants/MavenSearchRepositoryType.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/exception/RestSearchResponseException.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/AbstractSearchResponse.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/SearchResponseFactory.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/jfrog/JfrogSearchResponse.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/jfrog/JfrogSearchResponseResult.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponse.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponseDoc.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/maven/MavenSearchResponseResponse.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponeArtifactLinks.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponse.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponseArtifactHits.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus2/Nexus2SearchResponseData.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponse.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponseAsset.java create mode 100644 cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/to/nexus3/Nexus3SearchResponseItem.java create mode 100644 cobigen/cobigen-core-api/src/test/java/com/devonfw/cobigen/api/MavenUtilTest.java create mode 100644 cobigen/cobigen-core-api/src/test/resources/logback-test.xml create mode 100644 cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/jfrogJsonTest.json create mode 100644 cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/mavenJsonTest.json create mode 100644 cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/nexus2JsonTest.json create mode 100644 cobigen/cobigen-core-api/src/test/resources/testdata/unittest/MavenUtilTest/nexus3JsonTest.json 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