From 9b2164fbf6b317512d3946e6e00af7d372262c4b Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Tue, 29 Oct 2024 16:56:19 -0700 Subject: [PATCH] Delete contentful references See gh-21 --- build.gradle | 1 - .../io/spring/projectapi/Application.java | 19 -- .../projectapi/ApplicationProperties.java | 68 +--- .../contentful/ContentfulException.java | 34 -- .../contentful/ContentfulOperations.java | 142 -------- .../contentful/ContentfulQueries.java | 109 ------- .../contentful/ContentfulService.java | 76 ----- ...validContentfulQueryResponseException.java | 43 --- ...fulProjectDocumentationFoundException.java | 55 ---- .../NoSuchContentfulProjectException.java | 55 ---- .../NoUniqueContentfulProjectException.java | 47 --- .../spring/projectapi/contentful/Project.java | 94 ------ .../projectapi/contentful/ProjectDetails.java | 43 --- .../contentful/ProjectDocumentation.java | 109 ------- .../projectapi/contentful/ProjectSupport.java | 86 ----- .../contentful/RetryInterceptor.java | 64 ---- .../projectapi/contentful/package-info.java | 20 -- src/main/resources/application.properties | 5 - .../contentful/ContentfulOperationsTests.java | 307 ------------------ .../contentful/ContentfulQueriesTests.java | 178 ---------- .../contentful/ContentfulServiceTests.java | 109 ------- .../ProjectDocumentationJsonTests.java | 76 ----- .../contentful/ProjectJsonTests.java | 68 ---- .../contentful/ProjectSupportJsonTests.java | 52 --- .../contentful/RetryInterceptorTests.java | 115 ------- 25 files changed, 1 insertion(+), 1974 deletions(-) delete mode 100644 src/main/java/io/spring/projectapi/contentful/ContentfulException.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/ContentfulOperations.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/ContentfulQueries.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/ContentfulService.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/InvalidContentfulQueryResponseException.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/NoSuchContentfulProjectDocumentationFoundException.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/NoSuchContentfulProjectException.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/NoUniqueContentfulProjectException.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/Project.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/ProjectDetails.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/ProjectDocumentation.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/ProjectSupport.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/RetryInterceptor.java delete mode 100644 src/main/java/io/spring/projectapi/contentful/package-info.java delete mode 100644 src/test/java/io/spring/projectapi/contentful/ContentfulOperationsTests.java delete mode 100644 src/test/java/io/spring/projectapi/contentful/ContentfulQueriesTests.java delete mode 100644 src/test/java/io/spring/projectapi/contentful/ContentfulServiceTests.java delete mode 100644 src/test/java/io/spring/projectapi/contentful/ProjectDocumentationJsonTests.java delete mode 100644 src/test/java/io/spring/projectapi/contentful/ProjectJsonTests.java delete mode 100644 src/test/java/io/spring/projectapi/contentful/ProjectSupportJsonTests.java delete mode 100644 src/test/java/io/spring/projectapi/contentful/RetryInterceptorTests.java diff --git a/build.gradle b/build.gradle index 6fb1d56..c334aab 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-hateoas' implementation 'org.springframework.boot:spring-boot-starter-graphql' implementation 'com.azure.spring:spring-cloud-azure-starter-keyvault-secrets' - implementation 'com.contentful.java:cma-sdk:3.4.12' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' implementation 'com.vladsch.flexmark:flexmark-all:0.64.8' implementation 'org.apache.maven:maven-artifact:3.6.3' diff --git a/src/main/java/io/spring/projectapi/Application.java b/src/main/java/io/spring/projectapi/Application.java index 0d09259..6dd671f 100644 --- a/src/main/java/io/spring/projectapi/Application.java +++ b/src/main/java/io/spring/projectapi/Application.java @@ -17,9 +17,7 @@ package io.spring.projectapi; import com.fasterxml.jackson.databind.ObjectMapper; -import io.spring.projectapi.ApplicationProperties.Contentful; import io.spring.projectapi.ApplicationProperties.Github; -import io.spring.projectapi.contentful.ContentfulService; import io.spring.projectapi.github.GithubOperations; import org.springframework.boot.SpringApplication; @@ -27,28 +25,11 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; -import org.springframework.web.reactive.function.client.WebClient; @SpringBootApplication @EnableConfigurationProperties(ApplicationProperties.class) public class Application { - private static final String BASE_URL = "https://graphql.contentful.com/content/v1/spaces/%s/environments/%s"; - - @Bean - public ContentfulService contentfulService(ObjectMapper objectMapper, WebClient.Builder webClientBuilder, - ApplicationProperties properties) { - Contentful contentful = properties.getContentful(); - String accessToken = contentful.getAccessToken(); - String contentManagementToken = contentful.getContentManagementToken(); - String spaceId = contentful.getSpaceId(); - String environmentId = contentful.getEnvironmentId(); - String baseUrl = BASE_URL.formatted(spaceId, environmentId); - WebClient webClient = webClientBuilder.baseUrl(baseUrl).build(); - return new ContentfulService(objectMapper, webClient, accessToken, contentManagementToken, spaceId, - environmentId); - } - @Bean public GithubOperations githubOperations(RestTemplateBuilder builder, ObjectMapper objectMapper, ApplicationProperties properties) { diff --git a/src/main/java/io/spring/projectapi/ApplicationProperties.java b/src/main/java/io/spring/projectapi/ApplicationProperties.java index 104062e..8c5abe4 100644 --- a/src/main/java/io/spring/projectapi/ApplicationProperties.java +++ b/src/main/java/io/spring/projectapi/ApplicationProperties.java @@ -28,83 +28,17 @@ @ConfigurationProperties(prefix = "projects") public class ApplicationProperties { - private final Contentful contentful; - private final Github github; @ConstructorBinding - ApplicationProperties(@DefaultValue Contentful contentful, @DefaultValue Github github) { - this.contentful = contentful; + ApplicationProperties(@DefaultValue Github github) { this.github = github; } - public Contentful getContentful() { - return this.contentful; - } - public Github getGithub() { return this.github; } - /** - * Properties to access Contentful's API. - */ - public static class Contentful { - - /** - * Access token for Contentful's Content Delivery API. - * - * @see Content - * Delivery API - */ - private String accessToken; - - /** - * Access token for Contentful's Content Management API. - * - * @see Content - * Management API - */ - private String contentManagementToken; - - /** - * Contentful space id. - */ - private String spaceId; - - /** - * Contentful environment id. - */ - private String environmentId; - - @ConstructorBinding - Contentful(String accessToken, String contentManagementToken, String spaceId, String environmentId) { - this.accessToken = accessToken; - this.contentManagementToken = contentManagementToken; - this.spaceId = spaceId; - this.environmentId = environmentId; - } - - public String getAccessToken() { - return this.accessToken; - } - - public String getSpaceId() { - return this.spaceId; - } - - public String getEnvironmentId() { - return this.environmentId; - } - - public String getContentManagementToken() { - return this.contentManagementToken; - } - - } - /** * Properties to decide Github team membership. */ diff --git a/src/main/java/io/spring/projectapi/contentful/ContentfulException.java b/src/main/java/io/spring/projectapi/contentful/ContentfulException.java deleted file mode 100644 index 15c68a9..0000000 --- a/src/main/java/io/spring/projectapi/contentful/ContentfulException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -/** - * {@link RuntimeException} indicating a problem with Contentful. - * - * @author Phillip Webb - */ -public class ContentfulException extends RuntimeException { - - ContentfulException(String message) { - super(message); - } - - ContentfulException(Throwable cause) { - super(cause); - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/ContentfulOperations.java b/src/main/java/io/spring/projectapi/contentful/ContentfulOperations.java deleted file mode 100644 index c1c61c5..0000000 --- a/src/main/java/io/spring/projectapi/contentful/ContentfulOperations.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import com.contentful.java.cma.CMAClient; -import com.contentful.java.cma.model.CMAArray; -import com.contentful.java.cma.model.CMAEntry; -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.OkHttpClient; - -/** - * Contentful operations performed via the {@link CMAClient REST API}. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -class ContentfulOperations { - - private static final String LOCALE = "en-US"; - - private static Comparator> VERSION_COMPARATOR = (o1, o2) -> { - String version1 = (String) o1.get("version"); - String version2 = (String) o2.get("version"); - return -version1.compareTo(version2); - }; - - private final CMAClient client; - - private final ObjectMapper objectMapper; - - ContentfulOperations(ObjectMapper objectMapper, String accessToken, String spaceId, String environmentId) { - this(objectMapper, buildClient(accessToken, spaceId, environmentId)); - } - - ContentfulOperations(ObjectMapper objectMapper, CMAClient client) { - this.objectMapper = objectMapper; - this.client = client; - } - - private static CMAClient buildClient(String accessToken, String spaceId, String environmentId) { - CMAClient.Builder builder = new CMAClient.Builder(); - builder.setAccessToken(accessToken); - builder.setSpaceId(spaceId); - builder.setEnvironmentId(environmentId); - OkHttpClient coreCallFactory = builder.defaultCoreCallFactoryBuilder() - .addInterceptor(new RetryInterceptor()) - .build(); - builder.setCoreCallFactory(coreCallFactory); - return builder.build(); - } - - void addProjectDocumentation(String projectSlug, ProjectDocumentation documentation) { - CMAEntry projectEntry = getProjectEntry(projectSlug); - List> releases = projectEntry.getField("documentation", LOCALE); - Map documentationMap = convertToMap(documentation); - releases.add(documentationMap); - computeCurrentRelease(releases); - CMAEntry updated = this.client.entries().update(projectEntry); - this.client.entries().publish(updated); - } - - void patchProjectDetails(String projectSlug, ProjectDetails projectDetails) { - CMAEntry projectEntry = getProjectEntry(projectSlug); - updateIfPresent(projectDetails.getSpringBootConfig(), projectEntry, "springBootConfig"); - updateIfPresent(projectDetails.getBody(), projectEntry, "body"); - CMAEntry updated = this.client.entries().update(projectEntry); - this.client.entries().publish(updated); - } - - private static void updateIfPresent(String value, CMAEntry projectEntry, String field) { - if (value != null) { - projectEntry.setField(field, LOCALE, value); - } - } - - private void computeCurrentRelease(List> releases) { - List> modifiableReleases = releases.stream() - .map(initializeToFalse()) - .collect(Collectors.toList()); - releases.clear(); - releases.addAll(modifiableReleases); - modifiableReleases.stream() - .sorted(VERSION_COMPARATOR) - .filter((documentation) -> "GENERAL_AVAILABILITY".equals(documentation.get("status"))) - .findFirst() - .ifPresent((documentation) -> documentation.put("current", true)); - } - - private static Function, LinkedHashMap> initializeToFalse() { - return (map) -> { - LinkedHashMap modifiableMap = new LinkedHashMap<>(map); - modifiableMap.put("current", false); - return modifiableMap; - }; - } - - void deleteDocumentation(String projectSlug, String version) { - CMAEntry projectEntry = getProjectEntry(projectSlug); - List> documentations = projectEntry.getField("documentation", LOCALE); - NoSuchContentfulProjectDocumentationFoundException.throwIfHasNotPresent(documentations, projectSlug, version); - documentations.removeIf((documentation) -> documentation.get("version").equals(version)); - computeCurrentRelease(documentations); - CMAEntry updated = this.client.entries().update(projectEntry); - this.client.entries().publish(updated); - } - - @SuppressWarnings("unchecked") - private Map convertToMap(ProjectDocumentation documentation) { - return this.objectMapper.convertValue(documentation, Map.class); - } - - private CMAEntry getProjectEntry(String projectSlug) { - Map query = Map.of("content_type", "project", "fields.slug", projectSlug); - CMAArray entries = this.client.entries().fetchAll(query); - List items = entries.getItems(); - NoSuchContentfulProjectException.throwIfEmpty(items, projectSlug); - NoUniqueContentfulProjectException.throwIfNoUniqueResult(items, projectSlug); - return items.get(0); - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/ContentfulQueries.java b/src/main/java/io/spring/projectapi/contentful/ContentfulQueries.java deleted file mode 100644 index 6f9a7d6..0000000 --- a/src/main/java/io/spring/projectapi/contentful/ContentfulQueries.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; - -import org.springframework.graphql.client.ClientGraphQlResponse; -import org.springframework.graphql.client.ClientResponseField; -import org.springframework.graphql.client.GraphQlClient; -import org.springframework.graphql.client.GraphQlClient.RequestSpec; -import org.springframework.graphql.client.GraphQlClientException; -import org.springframework.graphql.client.HttpGraphQlClient; -import org.springframework.web.reactive.function.client.WebClient; - -/** - * Contentful queries performed via the GraphQL API. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -class ContentfulQueries { - - private static final Duration TIMEOUT = Duration.ofSeconds(1); - - private final GraphQlClient client; - - ContentfulQueries(WebClient webClient, String accessToken) { - this.client = HttpGraphQlClient.builder(webClient) - .headers((headers) -> headers.setBearerAuth(accessToken)) - .build(); - } - - List getProjects() { - ClientGraphQlResponse response = execute("projects"); - return fieldToEntityList(response, "projectCollection.items", Project.class); - } - - Project getProject(String projectSlug) { - ClientGraphQlResponse response = executeForSingleProject("project", projectSlug); - return fieldToEntity(response, "projectCollection.items[0]", Project.class); - } - - List getProjectDocumentations(String projectSlug) { - ClientGraphQlResponse response = executeForSingleProject("project-documentations", projectSlug); - return fieldToEntityList(response, "projectCollection.items[0].documentation", ProjectDocumentation.class); - } - - List getProjectSupports(String projectSlug) { - ClientGraphQlResponse response = executeForSingleProject("project-supports", projectSlug); - return fieldToEntityList(response, "projectCollection.items[0].support", ProjectSupport.class); - } - - private ClientGraphQlResponse execute(String documentName) { - return execute(this.client.documentName(documentName)); - } - - private ClientGraphQlResponse executeForSingleProject(String documentName, String projectSlug) { - ClientGraphQlResponse response = execute(this.client.documentName(documentName).variable("slug", projectSlug)); - NoSuchContentfulProjectException.throwIfHasNoValue(response.field("projectCollection.items[0]"), projectSlug); - return response; - } - - private List fieldToEntityList(ClientGraphQlResponse response, String path, Class type) { - return field(response, path, (field) -> getList(type, response, path)); - } - - private static List getList(Class type, ClientGraphQlResponse response, String path) { - ClientResponseField field = response.field(path); - return (!field.hasValue() && response.getErrors().isEmpty()) ? Collections.emptyList() - : field.toEntityList(type); - } - - private T fieldToEntity(ClientGraphQlResponse response, String path, Class type) { - return field(response, path, (field) -> field.toEntity(type)); - } - - private T field(ClientGraphQlResponse response, String path, Function adapter) { - try { - return adapter.apply(response.field(path)); - } - catch (GraphQlClientException ex) { - throw new ContentfulException(ex); - } - } - - private ClientGraphQlResponse execute(RequestSpec request) { - ClientGraphQlResponse response = request.execute().block(TIMEOUT); - InvalidContentfulQueryResponseException.throwIfInvalid(response); - return response; - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/ContentfulService.java b/src/main/java/io/spring/projectapi/contentful/ContentfulService.java deleted file mode 100644 index 68339a4..0000000 --- a/src/main/java/io/spring/projectapi/contentful/ContentfulService.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.util.List; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.springframework.web.reactive.function.client.WebClient; - -/** - * Central class for interacting with Contentful's REST and GraphQL API. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -public class ContentfulService { - - private final ContentfulQueries queries; - - private final ContentfulOperations operations; - - public ContentfulService(ObjectMapper objectMapper, WebClient webClient, String accessToken, - String contentManagementToken, String spaceId, String environmentId) { - this.queries = new ContentfulQueries(webClient, accessToken); - this.operations = new ContentfulOperations(objectMapper, contentManagementToken, spaceId, environmentId); - } - - ContentfulService(ContentfulQueries queries, ContentfulOperations operations) { - this.queries = queries; - this.operations = operations; - } - - public List getProjects() { - return this.queries.getProjects(); - } - - public Project getProject(String projectSlug) { - return this.queries.getProject(projectSlug); - } - - public List getProjectDocumentations(String projectSlug) { - return this.queries.getProjectDocumentations(projectSlug); - } - - public List getProjectSupports(String projectSlug) { - return this.queries.getProjectSupports(projectSlug); - } - - public void addProjectDocumentation(String projectSlug, ProjectDocumentation documentation) { - this.operations.addProjectDocumentation(projectSlug, documentation); - } - - public void deleteDocumentation(String projectSlug, String version) { - this.operations.deleteDocumentation(projectSlug, version); - } - - public void patchProjectDetails(String projectSlug, ProjectDetails details) { - this.operations.patchProjectDetails(projectSlug, details); - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/InvalidContentfulQueryResponseException.java b/src/main/java/io/spring/projectapi/contentful/InvalidContentfulQueryResponseException.java deleted file mode 100644 index 5d9d78f..0000000 --- a/src/main/java/io/spring/projectapi/contentful/InvalidContentfulQueryResponseException.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import org.springframework.graphql.client.ClientGraphQlResponse; - -/** - * {@link ContentfulException} thrown when an invalid response is received. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -public class InvalidContentfulQueryResponseException extends ContentfulException { - - InvalidContentfulQueryResponseException(Throwable cause) { - super(cause); - } - - InvalidContentfulQueryResponseException(String message) { - super(message); - } - - static void throwIfInvalid(ClientGraphQlResponse response) { - if (response == null || !response.isValid()) { - throw new InvalidContentfulQueryResponseException("Empty or invalid contentful response"); - } - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/NoSuchContentfulProjectDocumentationFoundException.java b/src/main/java/io/spring/projectapi/contentful/NoSuchContentfulProjectDocumentationFoundException.java deleted file mode 100644 index 55723af..0000000 --- a/src/main/java/io/spring/projectapi/contentful/NoSuchContentfulProjectDocumentationFoundException.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.util.List; -import java.util.Map; - -/** - * {@link ContentfulException} thrown when a project documentation with the specified - * version does not exist. - * - * @author Madhura Bhave - */ -public final class NoSuchContentfulProjectDocumentationFoundException extends ContentfulException { - - private final String projectSlug; - - private final String version; - - NoSuchContentfulProjectDocumentationFoundException(String projectSlug, String version) { - super("No contentful documentation found for slug '%s' and version '%s'".formatted(projectSlug, version)); - this.projectSlug = projectSlug; - this.version = version; - } - - static void throwIfHasNotPresent(List> documentations, String projectSlug, String version) { - documentations.stream() - .filter((m) -> m.get("version").equals(version)) - .findFirst() - .orElseThrow((() -> new NoSuchContentfulProjectDocumentationFoundException(projectSlug, version))); - } - - public String getProjectSlug() { - return this.projectSlug; - } - - public String getVersion() { - return this.version; - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/NoSuchContentfulProjectException.java b/src/main/java/io/spring/projectapi/contentful/NoSuchContentfulProjectException.java deleted file mode 100644 index 12e3e24..0000000 --- a/src/main/java/io/spring/projectapi/contentful/NoSuchContentfulProjectException.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.util.Collection; - -import org.springframework.graphql.client.ClientResponseField; - -/** - * {@link ContentfulException} thrown when a project with the specified slug does not - * exist. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -public final class NoSuchContentfulProjectException extends ContentfulException { - - private final String projectSlug; - - private NoSuchContentfulProjectException(String projectSlug) { - super("No contentful project found for slug '%s'".formatted(projectSlug)); - this.projectSlug = projectSlug; - } - - public String getProjectSlug() { - return this.projectSlug; - } - - static void throwIfEmpty(Collection items, String projectSlug) { - if (items.isEmpty()) { - throw new NoSuchContentfulProjectException(projectSlug); - } - } - - static void throwIfHasNoValue(ClientResponseField field, String projectSlug) { - if (!field.hasValue()) { - throw new NoSuchContentfulProjectException(projectSlug); - } - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/NoUniqueContentfulProjectException.java b/src/main/java/io/spring/projectapi/contentful/NoUniqueContentfulProjectException.java deleted file mode 100644 index fc03d5e..0000000 --- a/src/main/java/io/spring/projectapi/contentful/NoUniqueContentfulProjectException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.util.Collection; - -/** - * {@link ContentfulException} thrown when more than one project exists with the specified - * slug. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -public final class NoUniqueContentfulProjectException extends ContentfulException { - - private final String projectSlug; - - private NoUniqueContentfulProjectException(String projectSlug) { - super("No unique contentful project found for slug '%s'".formatted(projectSlug)); - this.projectSlug = projectSlug; - } - - public String getProjectSlug() { - return this.projectSlug; - } - - static void throwIfNoUniqueResult(Collection items, String projectSlug) { - if (items.size() > 1) { - throw new NoUniqueContentfulProjectException(projectSlug); - } - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/Project.java b/src/main/java/io/spring/projectapi/contentful/Project.java deleted file mode 100644 index 4a14a86..0000000 --- a/src/main/java/io/spring/projectapi/contentful/Project.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonCreator.Mode; - -import org.springframework.util.Assert; - -/** - * Java representation of the {@code project} type as defined in - * spring-io/spring-website. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -public class Project { - - private final String title; - - private final String slug; - - private final String github; - - private final Status status; - - @JsonCreator(mode = Mode.PROPERTIES) - public Project(String title, String slug, String github, Status status) { - Assert.notNull(title, "'title' must not be null"); - Assert.notNull(slug, "'slug' must not be null"); - this.title = title; - this.slug = slug; - this.github = github; - this.status = status; - } - - public String getTitle() { - return this.title; - } - - public String getSlug() { - return this.slug; - } - - public String getGithub() { - return this.github; - } - - public Status getStatus() { - return this.status; - } - - /** - * Project status. - */ - public enum Status { - - /** - * Incubating. - */ - INCUBATING, - - /** - * Active. - */ - ACTIVE, - - /** - * Community Maintained. - */ - COMMUNITY, - - /** - * End of Life. - */ - END_OF_LIFE - - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/ProjectDetails.java b/src/main/java/io/spring/projectapi/contentful/ProjectDetails.java deleted file mode 100644 index 769b2b8..0000000 --- a/src/main/java/io/spring/projectapi/contentful/ProjectDetails.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -/** - * Data holder for project details. - * - * @author Madhura Bhave - */ -public class ProjectDetails { - - private final String springBootConfig; - - private final String body; - - public ProjectDetails(String springBootConfig, String body) { - this.springBootConfig = springBootConfig; - this.body = body; - } - - public String getSpringBootConfig() { - return this.springBootConfig; - } - - public String getBody() { - return this.body; - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/ProjectDocumentation.java b/src/main/java/io/spring/projectapi/contentful/ProjectDocumentation.java deleted file mode 100644 index 2083249..0000000 --- a/src/main/java/io/spring/projectapi/contentful/ProjectDocumentation.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonCreator.Mode; - -/** - * Java representation of the {@code project documentation} type as defined in - * spring-io/spring-website. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -public class ProjectDocumentation implements Comparable { - - private final String version; - - private final boolean antora; - - private final String api; - - private final String ref; - - private final Status status; - - private final boolean current; - - @JsonCreator(mode = Mode.PROPERTIES) - public ProjectDocumentation(String version, boolean antora, String api, String ref, Status status, - boolean current) { - this.version = version; - this.antora = antora; - this.api = api; - this.ref = ref; - this.status = status; - this.current = current; - } - - public String getVersion() { - return this.version; - } - - public String getApi() { - return this.api; - } - - public String getRef() { - return this.ref; - } - - public Status getStatus() { - return this.status; - } - - public boolean isCurrent() { - return this.current; - } - - @Override - public int compareTo(ProjectDocumentation other) { - if (other == null) { - return -1; - } - // invert Version comparator, to get most recent version first - return -this.version.compareTo(other.version); - } - - public boolean isAntora() { - return this.antora; - } - - /** - * Project documentation status. - */ - public enum Status { - - /** - * Snapshot. - */ - SNAPSHOT, - - /** - * Pre-release. - */ - PRERELEASE, - - /** - * General Availability. - */ - GENERAL_AVAILABILITY; - - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/ProjectSupport.java b/src/main/java/io/spring/projectapi/contentful/ProjectSupport.java deleted file mode 100644 index 22e5dec..0000000 --- a/src/main/java/io/spring/projectapi/contentful/ProjectSupport.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.time.LocalDate; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonCreator.Mode; -import com.fasterxml.jackson.annotation.JsonFormat; - -/** - * Java representation of the {@code project support} type as defined in - * spring-io/spring-website. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -public class ProjectSupport { - - private final String branch; - - @JsonFormat(pattern = "yyyy-MM-dd") - private final LocalDate initialDate; - - @JsonFormat(pattern = "yyyy-MM-dd") - private final LocalDate ossEnforcedEnd; - - @JsonFormat(pattern = "yyyy-MM-dd") - private final LocalDate ossPolicyEnd; - - @JsonFormat(pattern = "yyyy-MM-dd") - private final LocalDate commercialEnforcedEnd; - - @JsonFormat(pattern = "yyyy-MM-dd") - private final LocalDate commercialPolicyEnd; - - @JsonCreator(mode = Mode.PROPERTIES) - public ProjectSupport(String branch, LocalDate initialRelease, LocalDate ossEnforcedEnd, LocalDate ossPolicyEnd, - LocalDate commercialEnforcedEnd, LocalDate commercialPolicyEnd) { - this.branch = branch; - this.initialDate = initialRelease; - this.ossEnforcedEnd = ossEnforcedEnd; - this.ossPolicyEnd = ossPolicyEnd; - this.commercialEnforcedEnd = commercialEnforcedEnd; - this.commercialPolicyEnd = commercialPolicyEnd; - } - - public String getBranch() { - return this.branch; - } - - public LocalDate getInitialDate() { - return this.initialDate; - } - - public LocalDate getOssEnforcedEnd() { - return this.ossEnforcedEnd; - } - - public LocalDate getOssPolicyEnd() { - return this.ossPolicyEnd; - } - - public LocalDate getCommercialEnforcedEnd() { - return this.commercialEnforcedEnd; - } - - public LocalDate getCommercialPolicyEnd() { - return this.commercialPolicyEnd; - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/RetryInterceptor.java b/src/main/java/io/spring/projectapi/contentful/RetryInterceptor.java deleted file mode 100644 index e058f02..0000000 --- a/src/main/java/io/spring/projectapi/contentful/RetryInterceptor.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.io.IOException; -import java.time.Duration; -import java.util.List; -import java.util.Map; - -import com.contentful.java.cma.model.RateLimits; -import okhttp3.Headers; -import okhttp3.Interceptor; -import okhttp3.Response; - -/** - * OkHttp {@link Interceptor} which can retry a request if the rate limit is exceeded. - * - * @author Madhura Bhave - */ -class RetryInterceptor implements Interceptor { - - @Override - public Response intercept(Chain chain) throws IOException { - Response response = chain.proceed(chain.request()); - int reset = getResetSeconds(response); - if (response.isSuccessful() || reset == 0) { - return response; - } - sleep(reset); - response.close(); - return chain.call().clone().execute(); - } - - private static int getResetSeconds(Response response) { - Headers headers = response.headers(); - Map> mappedHeaders = headers.toMultimap(); - RateLimits limits = new RateLimits.DefaultParser().parse(mappedHeaders); - return limits.getReset(); - } - - private void sleep(int seconds) { - try { - Thread.sleep(Duration.ofSeconds(seconds).toMillis()); - } - catch (InterruptedException interruptedException) { - Thread.currentThread().interrupt(); - } - } - -} diff --git a/src/main/java/io/spring/projectapi/contentful/package-info.java b/src/main/java/io/spring/projectapi/contentful/package-info.java deleted file mode 100644 index 82e9386..0000000 --- a/src/main/java/io/spring/projectapi/contentful/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Contentful API. - */ -package io.spring.projectapi.contentful; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f1ccdab..757ea81 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,11 +1,6 @@ spring.config.import=optional:secret.properties spring.jackson.serialization.indent_output=true -projects.contentful.space-id: ${projects-contentful-spaceId} -projects.contentful.environment-id: ${projects-contentful-environmentId} -projects.contentful.access-token: ${projects-contentful-accessToken} -projects.contentful.content-management-token: ${projects-contentful-contentManagementToken} - projects.github.org: spring-io projects.github.team: spring-team projects.github.accesstoken: ${projects-github-accessToken} diff --git a/src/test/java/io/spring/projectapi/contentful/ContentfulOperationsTests.java b/src/test/java/io/spring/projectapi/contentful/ContentfulOperationsTests.java deleted file mode 100644 index 094f1cc..0000000 --- a/src/test/java/io/spring/projectapi/contentful/ContentfulOperationsTests.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import com.contentful.java.cma.CMAClient; -import com.contentful.java.cma.ModuleEntries; -import com.contentful.java.cma.model.CMAArray; -import com.contentful.java.cma.model.CMAEntry; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.spring.projectapi.contentful.ProjectDocumentation.Status; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link ContentfulOperations}. - * - * @author Madhura Bhave - */ -class ContentfulOperationsTests { - - private ContentfulOperations operations; - - private CMAClient client; - - private ObjectMapper objectMapper; - - @BeforeEach - void setup() { - this.objectMapper = new ObjectMapper(); - this.client = mock(CMAClient.class); - this.operations = new ContentfulOperations(this.objectMapper, this.client); - } - - @Test - void addProjectDocumentationWhenProjectDoesNotExistThrowsException() { - setupNonExistentProject(); - assertThatExceptionOfType(NoSuchContentfulProjectException.class).isThrownBy(() -> this.operations - .addProjectDocumentation("does-not-exist", getDocumentation("1.0", Status.GENERAL_AVAILABILITY))); - } - - @Test - void addProjectDocumentation() { - setupProject(addRelease()); - ProjectDocumentation documentation = getDocumentation("2.0", Status.GENERAL_AVAILABILITY); - this.operations.addProjectDocumentation("test-project", documentation); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - List> updatedEntry = value.getField("documentation", "en-US"); - assertThat(updatedEntry.size()).isEqualTo(2); - assertThat(updatedEntry).extracting((map) -> map.get("version")).containsExactly("1.0", "2.0"); - assertThat(updatedEntry).extracting((map) -> map.get("current")).containsExactly(false, true); - assertThat(updatedEntry).extracting((map) -> map.get("antora")).containsExactly(false, true); - } - - @Test - void addProjectDocumentationForNonCurrent() { - setupProject(addRelease()); - ProjectDocumentation documentation = getDocumentation("0.7", Status.GENERAL_AVAILABILITY); - this.operations.addProjectDocumentation("test-project", documentation); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - List> updatedEntry = value.getField("documentation", "en-US"); - assertThat(updatedEntry.size()).isEqualTo(2); - assertThat(updatedEntry).extracting((map) -> map.get("version")).containsExactly("1.0", "0.7"); - assertThat(updatedEntry).extracting((map) -> map.get("current")).containsExactly(true, false); - } - - @Test - void addFirstProjectDocumentationForGARelease() { - setupProject((maps) -> { - }); - ProjectDocumentation documentation = getDocumentation("1.0", Status.GENERAL_AVAILABILITY); - this.operations.addProjectDocumentation("test-project", documentation); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - List> updatedEntry = value.getField("documentation", "en-US"); - assertThat(updatedEntry.size()).isEqualTo(1); - assertThat(updatedEntry).extracting((map) -> map.get("version")).containsExactly("1.0"); - assertThat(updatedEntry).extracting((map) -> map.get("current")).containsExactly(true); - } - - @Test - void addFirstProjectDocumentationForNonGARelease() { - setupProject((maps) -> { - }); - ProjectDocumentation documentation = getDocumentation("1.0-M1", Status.PRERELEASE); - this.operations.addProjectDocumentation("test-project", documentation); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - List> updatedEntry = value.getField("documentation", "en-US"); - assertThat(updatedEntry.size()).isEqualTo(1); - assertThat(updatedEntry).extracting((map) -> map.get("version")).containsExactly("1.0-M1"); - assertThat(updatedEntry).extracting((map) -> map.get("current")).containsExactly(false); - } - - @Test - void deleteProjectDocumentationWhenProjectDoesNotExistThrowsException() { - setupNonExistentProject(); - assertThatExceptionOfType(NoSuchContentfulProjectException.class) - .isThrownBy(() -> this.operations.deleteDocumentation("does-not-exist", "1.0")); - } - - @Test - void deleteProjectDocumentationWhenProjectDocumentationDoesNotExistThrowsException() { - setupProject(addRelease()); - assertThatExceptionOfType(NoSuchContentfulProjectDocumentationFoundException.class) - .isThrownBy(() -> this.operations.deleteDocumentation("test-project", "2.0")); - } - - @Test - void deleteProjectDocumentation() { - setupProject((maps) -> { - maps.add(getRelease(false)); - maps.add(Map.of("version", "2.0", "api", "http://api.com", "ref", "http://ref.com", "status", - "GENERAL_AVAILABILITY", "repository", "RELEASE", "current", true)); - }); - this.operations.deleteDocumentation("test-project", "1.0"); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - List> updatedEntry = value.getField("documentation", "en-US"); - assertThat(updatedEntry.size()).isEqualTo(1); - assertThat(updatedEntry).extracting((map) -> map.get("version")).containsExactly("2.0"); - assertThat(updatedEntry).extracting((map) -> map.get("current")).containsExactly(true); - } - - @Test - void deleteCurrentProjectDocumentation() { - setupProject((maps) -> { - maps.add(getRelease(false)); - maps.add(Map.of("version", "2.0", "api", "http://api.com", "ref", "http://ref.com", "status", - "GENERAL_AVAILABILITY", "repository", "RELEASE", "current", true)); - }); - this.operations.deleteDocumentation("test-project", "2.0"); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - List> updatedEntry = value.getField("documentation", "en-US"); - assertThat(updatedEntry.size()).isEqualTo(1); - assertThat(updatedEntry).extracting((map) -> map.get("version")).containsExactly("1.0"); - assertThat(updatedEntry).extracting((map) -> map.get("current")).containsExactly(true); - } - - @Test - void deletePreReleaseProjectDocumentation() { - setupProject((maps) -> { - maps.add(getRelease(true)); - maps.add(Map.of("version", "2.0-M1", "api", "http://api.com", "ref", "http://ref.com", "status", - "PRERELEASE", "repository", "RELEASE", "current", false)); - }); - this.operations.deleteDocumentation("test-project", "2.0-M1"); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - List> updatedEntry = value.getField("documentation", "en-US"); - assertThat(updatedEntry.size()).isEqualTo(1); - assertThat(updatedEntry).extracting((map) -> map.get("version")).containsExactly("1.0"); - assertThat(updatedEntry).extracting((map) -> map.get("current")).containsExactly(true); - } - - @Test - void patchProjectWhenProjectDoesNotExistThrowsException() { - setupNonExistentProject(); - assertThatExceptionOfType(NoSuchContentfulProjectException.class) - .isThrownBy(() -> this.operations.patchProjectDetails("does-not-exist", new ProjectDetails(null, null))); - } - - @Test - void patchProjectWhenSpringBootConfigNullDoesNotUpdate() { - setupProject((maps) -> { - maps.add(getRelease(false)); - maps.add(Map.of("version", "2.0", "api", "http://api.com", "ref", "http://ref.com", "status", - "GENERAL_AVAILABILITY", "repository", "RELEASE", "current", true)); - }); - this.operations.patchProjectDetails("test-project", new ProjectDetails(null, "new body")); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - String updatedBody = value.getField("body", "en-US"); - String springBootConfig = value.getField("springBootConfig", "en-US"); - assertThat(updatedBody).isEqualTo("new body"); - assertThat(springBootConfig).isEqualTo("sbc"); - } - - @Test - void patchProjectWhenBodyNullDoesNotUpdate() { - setupProject((maps) -> { - maps.add(getRelease(false)); - maps.add(Map.of("version", "2.0", "api", "http://api.com", "ref", "http://ref.com", "status", - "GENERAL_AVAILABILITY", "repository", "RELEASE", "current", true)); - }); - this.operations.patchProjectDetails("test-project", new ProjectDetails("new sbc", null)); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - String updatedBody = value.getField("body", "en-US"); - String springBootConfig = value.getField("springBootConfig", "en-US"); - assertThat(updatedBody).isEqualTo("existing body"); - assertThat(springBootConfig).isEqualTo("new sbc"); - } - - @Test - void patchProjectUpdatesBodyAndDescription() { - setupProject((maps) -> { - maps.add(getRelease(false)); - maps.add(Map.of("version", "2.0", "api", "http://api.com", "ref", "http://ref.com", "status", - "GENERAL_AVAILABILITY", "repository", "RELEASE", "current", true)); - }); - this.operations.patchProjectDetails("test-project", new ProjectDetails("new sbc", "new body")); - ArgumentCaptor captor = ArgumentCaptor.forClass(CMAEntry.class); - verify(this.client.entries()).update(any()); - verify(this.client.entries()).publish(captor.capture()); - CMAEntry value = captor.getValue(); - String updatedBody = value.getField("body", "en-US"); - String springBootConfig = value.getField("springBootConfig", "en-US"); - assertThat(updatedBody).isEqualTo("new body"); - assertThat(springBootConfig).isEqualTo("new sbc"); - } - - @SuppressWarnings("unchecked") - private void setupProject(Consumer>> consumer) { - ModuleEntries entries = mock(ModuleEntries.class); - CMAArray cmaArray = mock(CMAArray.class); - given(this.client.entries()).willReturn(entries); - given(entries.fetchAll(anyMap())).willReturn(cmaArray); - List projects = new ArrayList<>(); - CMAEntry projectEntry = getProjectEntry(consumer); - projects.add(projectEntry); - given(cmaArray.getItems()).willReturn(projects); - given(this.client.entries().update(projectEntry)).willReturn(projectEntry); - } - - private CMAEntry getProjectEntry(Consumer>> consumer) { - CMAEntry projectEntry = new CMAEntry(); - List> documentation = new ArrayList<>(); - consumer.accept(documentation); - projectEntry.setField("documentation", "en-US", documentation); - projectEntry.setField("springBootConfig", "en-US", "sbc"); - projectEntry.setField("body", "en-US", "existing body"); - return projectEntry; - } - - private Consumer>> addRelease() { - return (maps) -> maps.add(getRelease(true)); - } - - private static Map getRelease(boolean current) { - return Map.of("version", "1.0", "api", "http://api.com", "ref", "http://ref.com", "status", - "GENERAL_AVAILABILITY", "repository", "RELEASE", "current", current, "antora", false); - } - - @SuppressWarnings("unchecked") - private void setupNonExistentProject() { - ModuleEntries entries = mock(ModuleEntries.class); - CMAArray cmaArray = mock(CMAArray.class); - given(this.client.entries()).willReturn(entries); - given(entries.fetchAll(anyMap())).willReturn(cmaArray); - given(cmaArray.getItems()).willReturn(Collections.emptyList()); - } - - private ProjectDocumentation getDocumentation(String version, Status status) { - return new ProjectDocumentation(version, true, "http://api.com", "http://ref.com", status, false); - } - -} diff --git a/src/test/java/io/spring/projectapi/contentful/ContentfulQueriesTests.java b/src/test/java/io/spring/projectapi/contentful/ContentfulQueriesTests.java deleted file mode 100644 index e0f78ac..0000000 --- a/src/test/java/io/spring/projectapi/contentful/ContentfulQueriesTests.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; - -import okhttp3.HttpUrl; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okio.Buffer; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; -import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; -import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * Tests for {@link ContentfulQueries}. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -@SpringBootTest -class ContentfulQueriesTests { - - private static final String ACCESS_TOKEN = "000000"; - - @Autowired - private MockWebServer server; - - @Autowired - private ContentfulQueries contentfulQueries; - - @Test - void getProjectsReturnsProjects() throws IOException { - setupResponse("query-projects.json"); - List projects = this.contentfulQueries.getProjects(); - assertThat(projects.size()).isEqualTo(3); - assertThat(projects.get(0).getSlug()).isEqualTo("spring-xd"); - } - - @Test - void getProjectsWhenNoProjectsReturnsEmpty() throws IOException { - setupResponse("query-no-projects.json"); - List projects = this.contentfulQueries.getProjects(); - assertThat(projects).isEmpty(); - } - - @Test - void getProjectsWhenErrorThrowsException() throws IOException { - setupResponse("query-error.json"); - assertThatExceptionOfType(ContentfulException.class).isThrownBy(this.contentfulQueries::getProjects); - } - - @Test - void getProjectReturnsProject() throws IOException { - setupResponse("query-project.json"); - Project project = this.contentfulQueries.getProject("spring-xd"); - assertThat(project.getSlug()).isEqualTo("spring-xd"); - } - - @Test - void getProjectWhenNoNoProjectMatchThrowsException() throws IOException { - setupResponse("query-no-project.json"); - assertThatExceptionOfType(NoSuchContentfulProjectException.class) - .isThrownBy(() -> this.contentfulQueries.getProject("spring-xd")) - .satisfies((ex) -> assertThat(ex.getProjectSlug()).isEqualTo("spring-xd")); - } - - @Test - void getProjectWhenErrorThrowsException() throws IOException { - setupResponse("query-error.json"); - assertThatExceptionOfType(ContentfulException.class) - .isThrownBy(() -> this.contentfulQueries.getProject("spring-xd")); - } - - @Test - void getProjectDocumentationsReturnsDocumentations() throws IOException { - setupResponse("query-project-documentations.json"); - List documenations = this.contentfulQueries.getProjectDocumentations("spring-xd"); - assertThat(documenations).hasSize(6); - assertThat(documenations.get(0).getVersion()).isEqualTo("3.0.0-SNAPSHOT"); - } - - @Test - void getProjectDocumentationsWhenNoProjectMatchThrowsException() throws IOException { - setupResponse("query-no-project.json"); - assertThatExceptionOfType(NoSuchContentfulProjectException.class) - .isThrownBy(() -> this.contentfulQueries.getProjectDocumentations("spring-xd")) - .satisfies((ex) -> assertThat(ex.getProjectSlug()).isEqualTo("spring-xd")); - } - - @Test - void getProjectSupportsReturnsSupports() throws IOException { - setupResponse("query-project-supports.json"); - List supports = this.contentfulQueries.getProjectSupports("spring-xd"); - assertThat(supports).hasSize(10); - assertThat(supports.get(0).getBranch()).isEqualTo("1.5.x"); - } - - @Test - void getProjectSupportsWhenNoProjectMatchThrowsException() throws IOException { - setupResponse("query-no-project.json"); - assertThatExceptionOfType(NoSuchContentfulProjectException.class) - .isThrownBy(() -> this.contentfulQueries.getProjectSupports("spring-xd")) - .satisfies((ex) -> assertThat(ex.getProjectSlug()).isEqualTo("spring-xd")); - } - - @Test - void getProjectSupportsWhenNullReturnsEmptyList() throws IOException { - setupResponse("query-null-support.json"); - List supports = this.contentfulQueries.getProjectSupports("spring-xd"); - assertThat(supports).isEmpty(); - } - - private void setupResponse(String name) throws IOException { - setupResponse(new ClassPathResource(name, getClass())); - } - - private void setupResponse(Resource resource) throws IOException { - try (InputStream inputStream = resource.getInputStream()) { - try (Buffer buffer = new Buffer()) { - buffer.readFrom(inputStream); - MockResponse response = new MockResponse(); - response.setBody(buffer); - response.setHeader("Content-Type", "application/json"); - this.server.enqueue(response); - } - } - } - - @Configuration(proxyBeanMethods = false) - @ImportAutoConfiguration({ JacksonAutoConfiguration.class, WebClientAutoConfiguration.class, - CodecsAutoConfiguration.class }) - static class Config { - - @Bean - MockWebServer mockWebServer() { - return new MockWebServer(); - } - - @Bean - ContentfulQueries contentfulQueries(MockWebServer mockWebServer, WebClient.Builder webClientBuilder) { - HttpUrl baseUrl = mockWebServer.url("/contentful.com"); - WebClient webClient = webClientBuilder.baseUrl(baseUrl.toString()).build(); - return new ContentfulQueries(webClient, ACCESS_TOKEN); - } - - } - -} diff --git a/src/test/java/io/spring/projectapi/contentful/ContentfulServiceTests.java b/src/test/java/io/spring/projectapi/contentful/ContentfulServiceTests.java deleted file mode 100644 index cbf0fe0..0000000 --- a/src/test/java/io/spring/projectapi/contentful/ContentfulServiceTests.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.time.LocalDate; -import java.util.List; - -import io.spring.projectapi.contentful.Project.Status; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link ContentfulService}. - * - * @author Madhura Bhave - */ -class ContentfulServiceTests { - - private ContentfulService service; - - private ContentfulQueries queries = mock(ContentfulQueries.class); - - private ContentfulOperations operations = mock(ContentfulOperations.class); - - private static final Project PROJECT = new Project("Test Project", "test-project", "github", Status.ACTIVE); - - private static final ProjectDocumentation PROJECT_DOCUMENTATION = new ProjectDocumentation("2.0", false, - "http://api.com", "http://ref.com", ProjectDocumentation.Status.GENERAL_AVAILABILITY, false); - - private static final ProjectSupport PROJECT_SUPPORT = new ProjectSupport("2.2.x", LocalDate.parse("2020-02-01"), - LocalDate.parse("2020-02-02"), LocalDate.parse("2020-02-03"), LocalDate.parse("2020-02-04"), - LocalDate.parse("2020-02-05")); - - @BeforeEach - void setup() { - this.service = new ContentfulService(this.queries, this.operations); - } - - @Test - void getProjectsReturnsProjects() { - given(this.queries.getProjects()).willReturn(List.of(PROJECT)); - List projects = this.service.getProjects(); - verify(this.queries).getProjects(); - assertThat(projects).containsExactly(PROJECT); - } - - @Test - void getProjectReturnProject() { - given(this.queries.getProject("test-project")).willReturn(PROJECT); - Project project = this.service.getProject("test-project"); - assertThat(project).isEqualTo(PROJECT); - verify(this.queries).getProject("test-project"); - } - - @Test - void getProjectDocumentations() { - given(this.queries.getProjectDocumentations("test-project")).willReturn(List.of(PROJECT_DOCUMENTATION)); - List projectDocumentations = this.service.getProjectDocumentations("test-project"); - verify(this.queries).getProjectDocumentations("test-project"); - assertThat(projectDocumentations).containsExactly(PROJECT_DOCUMENTATION); - } - - @Test - void getProjectSupports() { - given(this.queries.getProjectSupports("test-project")).willReturn(List.of(PROJECT_SUPPORT)); - List projectSupports = this.service.getProjectSupports("test-project"); - verify(this.queries).getProjectSupports("test-project"); - assertThat(projectSupports).containsExactly(PROJECT_SUPPORT); - } - - @Test - void addProject() { - this.service.addProjectDocumentation("test-project", PROJECT_DOCUMENTATION); - verify(this.operations).addProjectDocumentation("test-project", PROJECT_DOCUMENTATION); - } - - @Test - void deleteProject() { - this.service.deleteDocumentation("test-project", "1.0"); - verify(this.operations).deleteDocumentation("test-project", "1.0"); - } - - @Test - void patchProjectDetails() { - ProjectDetails details = new ProjectDetails("spring-boot-config", "my-project"); - this.service.patchProjectDetails("test-project", details); - verify(this.operations).patchProjectDetails("test-project", details); - } - -} diff --git a/src/test/java/io/spring/projectapi/contentful/ProjectDocumentationJsonTests.java b/src/test/java/io/spring/projectapi/contentful/ProjectDocumentationJsonTests.java deleted file mode 100644 index d24b3ca..0000000 --- a/src/test/java/io/spring/projectapi/contentful/ProjectDocumentationJsonTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.spring.projectapi.contentful.ProjectDocumentation.Status; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.json.JacksonTester; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * JSON tests for {@link ProjectDocumentation}. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -@JsonTest -@AutoConfigureWebClient -class ProjectDocumentationJsonTests { - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private JacksonTester json; - - @Test - void convertValueToMapReturnsMap() { - ProjectDocumentation documentation = new ProjectDocumentation("ver", false, "api", "ref", - ProjectDocumentation.Status.PRERELEASE, true); - Map converted = this.objectMapper.convertValue(documentation, Map.class); - Map expected = new HashMap<>(); - expected.put("version", documentation.getVersion()); - expected.put("api", documentation.getApi()); - expected.put("ref", documentation.getRef()); - expected.put("status", documentation.getStatus().name()); - expected.put("current", documentation.isCurrent()); - expected.put("antora", documentation.isAntora()); - assertThat(converted).isEqualTo(expected); - } - - @Test - void readObjectReadsJson() throws Exception { - ProjectDocumentation projectDocumentation = this.json.readObject("project-documentation.json"); - assertThat(projectDocumentation.getVersion()).isEqualTo("3.0.0-SNAPSHOT"); - assertThat(projectDocumentation.getApi()).isEqualTo("https://docs.spring.io/spring-boot/docs/{version}/api/"); - assertThat(projectDocumentation.getRef()) - .isEqualTo("https://docs.spring.io/spring-boot/docs/{version}/reference/html/"); - assertThat(projectDocumentation.getStatus()).isEqualTo(Status.SNAPSHOT); - assertThat(projectDocumentation.isCurrent()).isEqualTo(true); - assertThat(projectDocumentation.isAntora()).isEqualTo(false); - } - -} diff --git a/src/test/java/io/spring/projectapi/contentful/ProjectJsonTests.java b/src/test/java/io/spring/projectapi/contentful/ProjectJsonTests.java deleted file mode 100644 index 6a2add9..0000000 --- a/src/test/java/io/spring/projectapi/contentful/ProjectJsonTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.io.IOException; - -import io.spring.projectapi.contentful.Project.Status; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.json.JacksonTester; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * JSON tests for {@link Project}. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -@JsonTest -@AutoConfigureWebClient -class ProjectJsonTests { - - @Autowired - private JacksonTester json; - - @Test - void readObjectReadsJson() throws IOException { - Project[] projects = this.json.readObject("project-all.json"); - assertThat(projects).hasSize(96); - } - - @Test - void readObjectWhenMinimalProjectReadsJson() throws IOException { - Project project = this.json.readObject("project-minimal.json")[0]; - assertThat(project.getTitle()).isEqualTo("test"); - assertThat(project.getSlug()).isEqualTo("test"); - assertThat(project.getGithub()).isNull(); - assertThat(project.getStatus()).isNull(); - } - - @Test - void readObjectWhenCompleteProjectReadsJson() throws IOException { - Project project = this.json.readObject("project-complete.json")[0]; - assertThat(project.getTitle()).isEqualTo("title"); - assertThat(project.getSlug()).isEqualTo("test"); - assertThat(project.getGithub()).isEqualTo("gh"); - assertThat(project.getStatus()).isEqualTo(Status.ACTIVE); - } - -} diff --git a/src/test/java/io/spring/projectapi/contentful/ProjectSupportJsonTests.java b/src/test/java/io/spring/projectapi/contentful/ProjectSupportJsonTests.java deleted file mode 100644 index cdf6ff0..0000000 --- a/src/test/java/io/spring/projectapi/contentful/ProjectSupportJsonTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.json.JacksonTester; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * JSON tests for {@link ProjectSupport}. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -@JsonTest -@AutoConfigureWebClient -class ProjectSupportJsonTests { - - @Autowired - private JacksonTester json; - - @Test - void readObjectReadsJson() throws Exception { - ProjectSupport projectSupport = this.json.readObject("project-support.json"); - assertThat(projectSupport.getBranch()).isEqualTo("1.5.x"); - assertThat(projectSupport.getInitialDate()).isEqualTo("2017-01-30"); - assertThat(projectSupport.getOssEnforcedEnd()).isEqualTo("2019-08-06"); - assertThat(projectSupport.getOssPolicyEnd()).isEqualTo("2019-08-07"); - assertThat(projectSupport.getCommercialEnforcedEnd()).isEqualTo("2019-08-08"); - assertThat(projectSupport.getCommercialPolicyEnd()).isEqualTo("2020-11-06"); - } - -} diff --git a/src/test/java/io/spring/projectapi/contentful/RetryInterceptorTests.java b/src/test/java/io/spring/projectapi/contentful/RetryInterceptorTests.java deleted file mode 100644 index f6eebe2..0000000 --- a/src/test/java/io/spring/projectapi/contentful/RetryInterceptorTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.spring.projectapi.contentful; - -import java.io.IOException; - -import okhttp3.Call; -import okhttp3.Interceptor.Chain; -import okhttp3.MediaType; -import okhttp3.Protocol; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.Response.Builder; -import okhttp3.ResponseBody; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.willReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -/** - * Tests for {@link RetryInterceptor}. - * - * @author Madhura Bhave - */ -class RetryInterceptorTests { - - private RetryInterceptor interceptor; - - @BeforeEach - void setup() { - this.interceptor = new RetryInterceptor(); - } - - @Test - void interceptWhenResponseSuccessfulShouldReturnResponse() throws Exception { - testNoRetry(200); - } - - @Test - void interceptWhenLimitResetHeaderNotSetShouldReturnResponse() throws Exception { - testNoRetry(400); - } - - @Test - void interceptWhenLimitResetGreaterThanZeroShouldRetry() throws Exception { - Chain chain = mock(Chain.class); - Request request = new Request.Builder().url("https://some-url.com").build(); - given(chain.request()).willReturn(request); - given(chain.toString()).willReturn(""); - Call call = setupForRetry(chain, request); - Response response = getResponse(request, 429, true); - given(chain.proceed(request)).willReturn(response); - this.interceptor.intercept(chain); - verifyRetry(chain, request, call); - } - - private static Call setupForRetry(Chain chain, Request request) { - Call call = mock(Call.class); - willReturn(call).given(chain).call(); - willReturn(call).given(call).clone(); - willReturn(request).given(call).request(); - return call; - } - - private static void verifyRetry(Chain chain, Request request, Call call) throws IOException { - verify(chain).request(); - verify(chain).proceed(request); - verify(call).execute(); - } - - private void testNoRetry(int code) throws IOException { - Chain chain = mock(Chain.class); - Request request = new Request.Builder().url("https://some-url.com").build(); - given(chain.request()).willReturn(request); - Response response = getResponse(request, code, false); - given(chain.proceed(request)).willReturn(response); - Response result = this.interceptor.intercept(chain); - assertThat(result).isEqualTo(response); - verify(chain).request(); - verify(chain).proceed(request); - verifyNoMoreInteractions(chain); - } - - private static Response getResponse(Request request, int code, boolean addLimitReset) { - Builder builder = new Builder().request(request) - .protocol(Protocol.HTTP_2) - .message("") - .code(429) - .body(ResponseBody.create("{}", MediaType.get("application/json; charset=utf-8"))); - if (addLimitReset) { - builder.addHeader("X-Contentful-RateLimit-Reset", "1"); - } - return builder.build(); - } - -}