From af4ea496080855bd7a19db3f44ef4a638a0ff4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9da=20Housni=20Alaoui?= Date: Wed, 24 Jun 2020 11:27:36 +0200 Subject: [PATCH 1/4] Template variables SPI --- .../server/core/AdditionalUriHandler.java | 15 ++++++ .../hateoas/server/core/WebHandler.java | 13 ++--- .../server/mvc/UriComponentsContributor.java | 18 +++++++ .../server/mvc/WebMvcLinkBuilderFactory.java | 52 +++++++++++++++---- .../mvc/WebMvcLinkBuilderFactoryUnitTest.java | 26 ++++++++++ 5 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/springframework/hateoas/server/core/AdditionalUriHandler.java diff --git a/src/main/java/org/springframework/hateoas/server/core/AdditionalUriHandler.java b/src/main/java/org/springframework/hateoas/server/core/AdditionalUriHandler.java new file mode 100644 index 000000000..533456ad2 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/server/core/AdditionalUriHandler.java @@ -0,0 +1,15 @@ +package org.springframework.hateoas.server.core; + +import org.springframework.hateoas.TemplateVariables; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * @author Réda Housni Alaoui + */ +public interface AdditionalUriHandler { + + UriComponentsBuilder apply(UriComponentsBuilder uriComponentsBuilder, MethodInvocation methodInvocation); + + TemplateVariables apply(TemplateVariables templateVariables, UriComponents uriComponents, MethodInvocation methodInvocation); +} diff --git a/src/main/java/org/springframework/hateoas/server/core/WebHandler.java b/src/main/java/org/springframework/hateoas/server/core/WebHandler.java index 3e09138b5..26bf23de5 100644 --- a/src/main/java/org/springframework/hateoas/server/core/WebHandler.java +++ b/src/main/java/org/springframework/hateoas/server/core/WebHandler.java @@ -24,7 +24,6 @@ import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; @@ -56,6 +55,7 @@ * * @author Greg Turnquist * @author Oliver Drotbohm + * @author Réda Housni Alaoui */ public class WebHandler { @@ -75,12 +75,11 @@ public interface PreparedWebHandler { public static PreparedWebHandler linkTo(Object invocationValue, LinkBuilderCreator creator) { - return linkTo(invocationValue, creator, - (BiFunction) null); + return linkTo(invocationValue, creator, null); } public static T linkTo(Object invocationValue, LinkBuilderCreator creator, - @Nullable BiFunction additionalUriHandler, + @Nullable AdditionalUriHandler additionalUriHandler, Function finisher) { return linkTo(invocationValue, creator, additionalUriHandler).conclude(finisher); @@ -88,7 +87,7 @@ public static T linkTo(Object invocationValue, LinkBuild private static PreparedWebHandler linkTo(Object invocationValue, LinkBuilderCreator creator, - @Nullable BiFunction additionalUriHandler) { + @Nullable AdditionalUriHandler additionalUriHandler) { Assert.isInstanceOf(LastInvocationAware.class, invocationValue); @@ -150,7 +149,9 @@ private static PreparedWebHandler linkTo(Object invoc ? builder.buildAndExpand(values) // : additionalUriHandler.apply(builder, invocation).buildAndExpand(values); - TemplateVariables variables = NONE; + TemplateVariables variables = additionalUriHandler == null + ? NONE + : additionalUriHandler.apply(NONE, components, invocation); for (String parameter : optionalEmptyParameters) { diff --git a/src/main/java/org/springframework/hateoas/server/mvc/UriComponentsContributor.java b/src/main/java/org/springframework/hateoas/server/mvc/UriComponentsContributor.java index 47150cd2e..8a29d3073 100644 --- a/src/main/java/org/springframework/hateoas/server/mvc/UriComponentsContributor.java +++ b/src/main/java/org/springframework/hateoas/server/mvc/UriComponentsContributor.java @@ -15,10 +15,16 @@ */ package org.springframework.hateoas.server.mvc; +import java.util.Collection; +import java.util.Collections; + import org.springframework.core.MethodParameter; +import org.springframework.hateoas.TemplateVariable; +import org.springframework.hateoas.TemplateVariables; import org.springframework.hateoas.server.MethodLinkBuilderFactory; import org.springframework.lang.Nullable; import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** @@ -28,6 +34,7 @@ * * @see MethodLinkBuilderFactory#linkTo(Object) * @author Oliver Gierke + * @author Réda Housni Alaoui */ public interface UriComponentsContributor { @@ -47,4 +54,15 @@ public interface UriComponentsContributor { * @param value can be {@literal null}. */ void enhance(UriComponentsBuilder builder, @Nullable MethodParameter parameter, @Nullable Object value); + + /** + * Enhance the given {@link TemplateVariables} + * + * @param templateVariables will never be {@literal null}. + * @param uriComponents will never be {@literal null}. + * @param parameter can be {@literal null}. + */ + default TemplateVariables enhance(TemplateVariables templateVariables, UriComponents uriComponents, @Nullable MethodParameter parameter){ + return templateVariables; + } } diff --git a/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactory.java b/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactory.java index 69babfc3c..de971a162 100644 --- a/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactory.java +++ b/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactory.java @@ -26,10 +26,14 @@ import org.springframework.core.MethodParameter; import org.springframework.hateoas.Link; +import org.springframework.hateoas.TemplateVariables; import org.springframework.hateoas.server.MethodLinkBuilderFactory; +import org.springframework.hateoas.server.core.AdditionalUriHandler; import org.springframework.hateoas.server.core.LinkBuilderSupport; +import org.springframework.hateoas.server.core.MethodInvocation; import org.springframework.hateoas.server.core.MethodParameters; import org.springframework.hateoas.server.core.WebHandler; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** @@ -44,6 +48,7 @@ * @author Kevin Conaway * @author Andrew Naydyonock * @author Greg Turnquist + * @author Réda Housni Alaoui */ public class WebMvcLinkBuilderFactory implements MethodLinkBuilderFactory { @@ -106,8 +111,29 @@ public WebMvcLinkBuilder linkTo(Object invocationValue) { Function builderFactory = mapping -> UriComponentsBuilderFactory.getBuilder() .path(mapping); - return WebHandler.linkTo(invocationValue, WebMvcLinkBuilder::new, (builder, invocation) -> { + return WebHandler.linkTo(invocationValue, WebMvcLinkBuilder::new, + new UriComponentsContributorsAdditionalUriHandler(uriComponentsContributors), builderFactory); + } + /* + * (non-Javadoc) + * @see org.springframework.hateoas.MethodLinkBuilderFactory#linkTo(java.lang.reflect.Method, java.lang.Object[]) + */ + @Override + public WebMvcLinkBuilder linkTo(Method method, Object... parameters) { + return WebMvcLinkBuilder.linkTo(method, parameters); + } + + private static class UriComponentsContributorsAdditionalUriHandler implements AdditionalUriHandler { + + private final List uriComponentsContributors; + + private UriComponentsContributorsAdditionalUriHandler(List uriComponentsContributors) { + this.uriComponentsContributors = uriComponentsContributors; + } + + @Override + public UriComponentsBuilder apply(UriComponentsBuilder builder, MethodInvocation invocation) { MethodParameters parameters = MethodParameters.of(invocation.getMethod()); Iterator parameterValues = Arrays.asList(invocation.getArguments()).iterator(); @@ -123,16 +149,22 @@ public WebMvcLinkBuilder linkTo(Object invocationValue) { } return builder; + } - }, builderFactory); - } + @Override + public TemplateVariables apply(TemplateVariables templateVariables, UriComponents uriComponents, MethodInvocation invocation) { + MethodParameters parameters = MethodParameters.of(invocation.getMethod()); - /* - * (non-Javadoc) - * @see org.springframework.hateoas.MethodLinkBuilderFactory#linkTo(java.lang.reflect.Method, java.lang.Object[]) - */ - @Override - public WebMvcLinkBuilder linkTo(Method method, Object... parameters) { - return WebMvcLinkBuilder.linkTo(method, parameters); + for (MethodParameter parameter : parameters.getParameters()) { + + for (UriComponentsContributor contributor : uriComponentsContributors) { + if (contributor.supportsParameter(parameter)) { + templateVariables = contributor.enhance(templateVariables, uriComponents, parameter); + } + } + } + + return templateVariables; + } } } diff --git a/src/test/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactoryUnitTest.java b/src/test/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactoryUnitTest.java index a61cfeb9e..d1d98774b 100644 --- a/src/test/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactoryUnitTest.java +++ b/src/test/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactoryUnitTest.java @@ -31,6 +31,8 @@ import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; +import org.springframework.hateoas.TemplateVariable; +import org.springframework.hateoas.TemplateVariables; import org.springframework.hateoas.TestUtils; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilderUnitTest.ControllerWithMethods; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilderUnitTest.PersonControllerImpl; @@ -41,6 +43,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** @@ -50,6 +53,7 @@ * @author Oliver Gierke * @author Kamill Sokol * @author Ross Turner + * @author Réda Housni Alaoui */ class WebMvcLinkBuilderFactoryUnitTest extends TestUtils { @@ -174,6 +178,17 @@ void createsLinkToParameterizedControllerRootWithParameterMap() { assertThat(link.getHref()).endsWith("/people/17/addresses"); } + @Test + void appliesTemplateVariableIfContributorConfigured() { + + WebMvcLinkBuilderFactory factory = new WebMvcLinkBuilderFactory(); + factory.setUriComponentsContributors(Collections.singletonList(new SampleUriComponentsContributor())); + + Link link = factory.linkTo(methodOn(SampleController.class).sampleMethod(1L, null)).withSelfRel(); + assertPointsToMockServer(link); + assertThat(link.getHref()).endsWith("/sample/1{?foo}"); + } + interface SampleController { @RequestMapping("/sample/{id}") @@ -198,8 +213,19 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public void enhance(UriComponentsBuilder builder, MethodParameter parameter, Object value) { + if (value == null) { + return; + } builder.queryParam("foo", ((SpecialType) value).parameterValue); } + + @Override + public TemplateVariables enhance(TemplateVariables templateVariables, UriComponents uriComponents, MethodParameter parameter) { + if (uriComponents.getQueryParams().containsKey("foo")) { + return templateVariables; + } + return templateVariables.concat(TemplateVariable.requestParameter("foo")); + } } static class SpecialType { From ab2bbc0cde5ec279b57d5d13eef5c346b9fa8a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9da=20Housni=20Alaoui?= Date: Thu, 28 Jan 2021 10:07:24 +0100 Subject: [PATCH 2/4] Fix conflict --- .../hateoas/server/mvc/WebMvcLinkBuilderFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactory.java b/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactory.java index 6501598a5..0c0bb733f 100644 --- a/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactory.java +++ b/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilderFactory.java @@ -124,7 +124,7 @@ public WebMvcLinkBuilder linkTo(Object invocationValue) { .path(mapping); return WebHandler.linkTo(invocationValue, WebMvcLinkBuilder::new, - new UriComponentsContributorsAdditionalUriHandler(uriComponentsContributors), builderFactory); + new UriComponentsContributorsAdditionalUriHandler(uriComponentsContributors), builderFactory, getConversionService()); } /* From f31b2a7d819bde730e20e0d39350e703f8cda46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9da=20Housni=20Alaoui?= Date: Mon, 29 Aug 2022 09:39:01 +0200 Subject: [PATCH 3/4] Parameter will never be null --- .../hateoas/server/mvc/UriComponentsContributor.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/hateoas/server/mvc/UriComponentsContributor.java b/src/main/java/org/springframework/hateoas/server/mvc/UriComponentsContributor.java index 0af1f3cfb..08a1150db 100644 --- a/src/main/java/org/springframework/hateoas/server/mvc/UriComponentsContributor.java +++ b/src/main/java/org/springframework/hateoas/server/mvc/UriComponentsContributor.java @@ -15,11 +15,7 @@ */ package org.springframework.hateoas.server.mvc; -import java.util.Collection; -import java.util.Collections; - import org.springframework.core.MethodParameter; -import org.springframework.hateoas.TemplateVariable; import org.springframework.hateoas.TemplateVariables; import org.springframework.hateoas.server.MethodLinkBuilderFactory; import org.springframework.lang.Nullable; @@ -60,9 +56,9 @@ public interface UriComponentsContributor { * * @param templateVariables will never be {@literal null}. * @param uriComponents will never be {@literal null}. - * @param parameter can be {@literal null}. + * @param parameter will never be {@literal null}. */ - default TemplateVariables enhance(TemplateVariables templateVariables, UriComponents uriComponents, @Nullable MethodParameter parameter){ + default TemplateVariables enhance(TemplateVariables templateVariables, UriComponents uriComponents, MethodParameter parameter){ return templateVariables; } } From 38073ec6df7a5ef750d8052ba7c02374915e2b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9da=20Housni=20Alaoui?= Date: Sun, 21 May 2023 20:29:20 +0200 Subject: [PATCH 4/4] Missing License header on AdditionalUriHandler --- .../hateoas/server/core/AdditionalUriHandler.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/org/springframework/hateoas/server/core/AdditionalUriHandler.java b/src/main/java/org/springframework/hateoas/server/core/AdditionalUriHandler.java index 533456ad2..b1f1c72e4 100644 --- a/src/main/java/org/springframework/hateoas/server/core/AdditionalUriHandler.java +++ b/src/main/java/org/springframework/hateoas/server/core/AdditionalUriHandler.java @@ -1,3 +1,18 @@ +/* + * Copyright 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 org.springframework.hateoas.server.core; import org.springframework.hateoas.TemplateVariables;