From d40c28404d7ff85bbc9b91c75882e1c870727b95 Mon Sep 17 00:00:00 2001 From: Victor Mosin Date: Thu, 27 May 2021 12:06:54 +0200 Subject: [PATCH] fix(core): made sure self links at document level use service-url-provider as base url --- .../document/mapper/DocumentMapper.java | 8 ++- .../core/engine/internal/utils/UrlUtils.java | 6 +- .../crnk/core/engine/query/QueryContext.java | 8 +++ .../document/mapper/DocumentMapperTest.java | 7 ++- ...tomServiceUrlProviderApplicationTests.java | 61 +++++++++++++++++++ 5 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 crnk-integration-examples/spring-boot-example/src/test/java/io/crnk/example/springboot/simple/SpringBootCustomServiceUrlProviderApplicationTests.java diff --git a/crnk-core/src/main/java/io/crnk/core/engine/internal/document/mapper/DocumentMapper.java b/crnk-core/src/main/java/io/crnk/core/engine/internal/document/mapper/DocumentMapper.java index 028db93b7..fcf3b185e 100644 --- a/crnk-core/src/main/java/io/crnk/core/engine/internal/document/mapper/DocumentMapper.java +++ b/crnk-core/src/main/java/io/crnk/core/engine/internal/document/mapper/DocumentMapper.java @@ -19,6 +19,8 @@ import io.crnk.core.engine.information.resource.ResourceInformation; import io.crnk.core.engine.internal.utils.JsonApiUrlBuilder; import io.crnk.core.engine.internal.utils.PreconditionUtil; +import io.crnk.core.engine.internal.utils.StringUtils; +import io.crnk.core.engine.internal.utils.UrlUtils; import io.crnk.core.engine.properties.PropertiesProvider; import io.crnk.core.engine.query.QueryAdapter; import io.crnk.core.engine.query.QueryContext; @@ -136,14 +138,14 @@ private LinksInformation enrichSelfLink(LinksInformation linksInformation, Query HttpRequestContext requestContext = queryContext.getRequestContext(); if (requestContext != null && (linksInformation == null || linksInformation instanceof SelfLinksInformation)) { SelfLinksInformation selfLinksInformation = (SelfLinksInformation) linksInformation; - URI requestUri = requestContext.getRequestUri(); - if ((selfLinksInformation == null || selfLinksInformation.getSelf() == null) && requestUri != null) { + String requestUri = queryContext.getRequestUri(); + if ((selfLinksInformation == null || selfLinksInformation.getSelf() == null) && !StringUtils.isBlank(requestUri)) { if (selfLinksInformation == null) { selfLinksInformation = new DefaultSelfLinksInformation(); linksInformation = selfLinksInformation; } - JsonApiUrlBuilder.UrlParameterBuilder urlBuilder = new JsonApiUrlBuilder.UrlParameterBuilder(requestUri.toString()); + JsonApiUrlBuilder.UrlParameterBuilder urlBuilder = new JsonApiUrlBuilder.UrlParameterBuilder(requestUri); urlBuilder.addQueryParameters(requestContext.getRequestParameters()); selfLinksInformation.setSelf(urlBuilder.toString()); } diff --git a/crnk-core/src/main/java/io/crnk/core/engine/internal/utils/UrlUtils.java b/crnk-core/src/main/java/io/crnk/core/engine/internal/utils/UrlUtils.java index 693282b65..17152d0f9 100644 --- a/crnk-core/src/main/java/io/crnk/core/engine/internal/utils/UrlUtils.java +++ b/crnk-core/src/main/java/io/crnk/core/engine/internal/utils/UrlUtils.java @@ -41,8 +41,10 @@ public static String concat(String baseUrl, String... paths) { StringBuilder builder = new StringBuilder(); builder.append(removeTrailingSlash(baseUrl)); for (String path : paths) { - builder.append('/'); - builder.append(removeLeadingSlash(path)); + if (path != null) { + builder.append('/'); + builder.append(removeLeadingSlash(path)); + } } return builder.toString(); } diff --git a/crnk-core/src/main/java/io/crnk/core/engine/query/QueryContext.java b/crnk-core/src/main/java/io/crnk/core/engine/query/QueryContext.java index a145fd238..ebab61296 100644 --- a/crnk-core/src/main/java/io/crnk/core/engine/query/QueryContext.java +++ b/crnk-core/src/main/java/io/crnk/core/engine/query/QueryContext.java @@ -5,6 +5,7 @@ import java.util.concurrent.ConcurrentHashMap; import io.crnk.core.engine.http.HttpRequestContext; +import io.crnk.core.engine.internal.utils.UrlUtils; import io.crnk.core.engine.registry.ResourceRegistry; import io.crnk.core.resource.annotations.JsonApiVersion; @@ -97,4 +98,11 @@ public void initializeDefaults(ResourceRegistry resourceRegistry) { setRequestVersion(resourceRegistry.getLatestVersion()); } } + + /** + * @return full URL contains of {@link #getBaseUrl()} and {@link #getRequestPath()} + */ + public String getRequestUri() { + return UrlUtils.concat(getBaseUrl(), getRequestPath()); + } } diff --git a/crnk-core/src/test/java/io/crnk/core/engine/internal/document/mapper/DocumentMapperTest.java b/crnk-core/src/test/java/io/crnk/core/engine/internal/document/mapper/DocumentMapperTest.java index 7412eec65..0b4b0f408 100644 --- a/crnk-core/src/test/java/io/crnk/core/engine/internal/document/mapper/DocumentMapperTest.java +++ b/crnk-core/src/test/java/io/crnk/core/engine/internal/document/mapper/DocumentMapperTest.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; import io.crnk.core.engine.document.Document; import io.crnk.core.engine.document.ErrorData; import io.crnk.core.engine.document.Relationship; @@ -76,9 +77,9 @@ public void testSerializeWithoutLinks() { public void testSerializeRootSelfLink() { Task task = createTask(2, "sample task"); QueryAdapter adapter = createAdapter(Task.class); - Mockito.when(container.getRequestContextBase().getRequestUri()).thenReturn(URI.create("http://localhost/api/foo")); +// Mockito.when(container.getRequestContextBase().getRequestUri()).thenReturn(URI.create("http://localhost/api/foo")); Document document = mapper.toDocument(toResponse(task), adapter, mappingConfig).get(); - Assert.assertEquals("http://localhost/api/foo", getLinkText(document.getLinks().get("self"))); + Assert.assertEquals("http://127.0.0.1", getLinkText(document.getLinks().get("self"))); } public static class TaskLinks implements LinksInformation { @@ -301,6 +302,8 @@ public void testCompactModeWithNullData() { queryAdapter.setCompactMode(false); Document standardDocument = mapper.toDocument(new JsonApiResponse(), queryAdapter, mappingConfig).get(); + // The only difference standard document only has is a self link + standardDocument.setLinks(null); Assert.assertEquals(standardDocument, compactDocument); } diff --git a/crnk-integration-examples/spring-boot-example/src/test/java/io/crnk/example/springboot/simple/SpringBootCustomServiceUrlProviderApplicationTests.java b/crnk-integration-examples/spring-boot-example/src/test/java/io/crnk/example/springboot/simple/SpringBootCustomServiceUrlProviderApplicationTests.java new file mode 100644 index 000000000..6463073e8 --- /dev/null +++ b/crnk-integration-examples/spring-boot-example/src/test/java/io/crnk/example/springboot/simple/SpringBootCustomServiceUrlProviderApplicationTests.java @@ -0,0 +1,61 @@ +package io.crnk.example.springboot.simple; + +import com.jayway.restassured.RestAssured; +import io.crnk.core.boot.CrnkBoot; +import io.crnk.example.springboot.SpringBootExampleApplication; +import org.junit.Test; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.http.HttpStatus.OK; + +/** + * Makes sure links are generated based on {@link io.crnk.core.engine.url.ServiceUrlProvider} implementation + */ +@SpringBootTest( + classes = { + SpringBootExampleApplication.class, + SpringBootCustomServiceUrlProviderApplicationTests.TestConfiguration.class + }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT +) +public class SpringBootCustomServiceUrlProviderApplicationTests extends BaseTest { + + @Configuration + static class TestConfiguration { + + @Component + class TenantCrnkBootInitBeanPostProcessor implements BeanPostProcessor { + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof CrnkBoot) { + ((CrnkBoot) bean).getModuleRegistry().getHttpRequestContextProvider().setServiceUrlProvider(() -> "http://some.org"); + } + + return bean; + } + } + } + + @Test + public void testFindMany() { + RestAssured.given().contentType("application/json").when().get("/api/tasks") + .then() + .body("links.self", equalTo("http://some.org/tasks")) + .body("data[0].links.self", equalTo("http://some.org/tasks/1")) + .body("data[1].links.self", equalTo("http://some.org/tasks/2")) + .body("data[2].links.self", equalTo("http://some.org/tasks/3")); + } + + @Test + public void testFindOne() { + RestAssured.given().contentType("application/json").when().get("/api/tasks/1") + .then() + .body("links.self", equalTo("http://some.org/tasks/1")); + } +}