Skip to content

Commit

Permalink
Add a template variables contribution SPI
Browse files Browse the repository at this point in the history
  • Loading branch information
reda-alaoui committed Dec 4, 2022
1 parent 850cba1 commit 71079bf
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -73,12 +72,11 @@ public interface PreparedWebHandler<T extends LinkBuilder> {

public static <T extends LinkBuilder> PreparedWebHandler<T> linkTo(Object invocationValue,
LinkBuilderCreator<T> creator) {
return linkTo(invocationValue, creator,
(BiFunction<UriComponentsBuilder, MethodInvocation, UriComponentsBuilder>) null);
return linkTo(invocationValue, creator, null);
}

public static <T extends LinkBuilder> T linkTo(Object invocationValue, LinkBuilderCreator<T> creator,
@Nullable BiFunction<UriComponentsBuilder, MethodInvocation, UriComponentsBuilder> additionalUriHandler,
@Nullable AdditionalUriHandler additionalUriHandler,
Function<UriMapping, UriComponentsBuilder> finisher, Supplier<ConversionService> conversionService) {

return linkTo(invocationValue, creator, additionalUriHandler).conclude(finisher,
Expand All @@ -87,7 +85,7 @@ public static <T extends LinkBuilder> T linkTo(Object invocationValue, LinkBuild

private static <T extends LinkBuilder> PreparedWebHandler<T> linkTo(Object invocationValue,
LinkBuilderCreator<T> creator,
@Nullable BiFunction<UriComponentsBuilder, MethodInvocation, UriComponentsBuilder> additionalUriHandler) {
@Nullable AdditionalUriHandler additionalUriHandler) {

Assert.isInstanceOf(LastInvocationAware.class, invocationValue);

Expand Down Expand Up @@ -177,7 +175,9 @@ private static <T extends LinkBuilder> PreparedWebHandler<T> 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) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
package org.springframework.hateoas.server.mvc;

import org.springframework.core.MethodParameter;
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;

/**
Expand All @@ -28,6 +30,7 @@
*
* @see MethodLinkBuilderFactory#linkTo(Object)
* @author Oliver Gierke
* @author Réda Housni Alaoui
*/
public interface UriComponentsContributor {

Expand All @@ -47,4 +50,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 will never be {@literal null}.
*/
default TemplateVariables enhance(TemplateVariables templateVariables, UriComponents uriComponents, MethodParameter parameter){
return templateVariables;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@
import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.DefaultFormattingConversionService;
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.SpringAffordanceBuilder;
import org.springframework.hateoas.server.core.UriMapping;
Expand All @@ -44,6 +47,7 @@
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.mvc.condition.NameValueExpression;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

/**
Expand Down Expand Up @@ -150,8 +154,20 @@ public WebMvcLinkBuilder linkTo(Object invocationValue) {
Function<UriMapping, UriComponentsBuilder> builderFactory = mapping -> UriComponentsBuilderFactory
.forMapping(mapping);

return WebHandler.linkTo(invocationValue, WebMvcLinkBuilder::new, (builder, invocation) -> {
return WebHandler.linkTo(invocationValue, WebMvcLinkBuilder::new,
new UriComponentsContributorsAdditionalUriHandler(uriComponentsContributors), builderFactory, getConversionService());
}

private static class UriComponentsContributorsAdditionalUriHandler implements AdditionalUriHandler {

private final List<UriComponentsContributor> uriComponentsContributors;

private UriComponentsContributorsAdditionalUriHandler(List<UriComponentsContributor> uriComponentsContributors) {
this.uriComponentsContributors = uriComponentsContributors;
}

@Override
public UriComponentsBuilder apply(UriComponentsBuilder builder, MethodInvocation invocation) {
String[] primaryParams = SpringAffordanceBuilder.DISCOVERER.getParams(invocation.getMethod());

if (primaryParams.length > 0) {
Expand Down Expand Up @@ -189,8 +205,23 @@ public WebMvcLinkBuilder linkTo(Object invocationValue) {
}

return builder;
}

@Override
public TemplateVariables apply(TemplateVariables templateVariables, UriComponents uriComponents, MethodInvocation invocation) {
MethodParameters parameters = MethodParameters.of(invocation.getMethod());

for (MethodParameter parameter : parameters.getParameters()) {

for (UriComponentsContributor contributor : uriComponentsContributors) {
if (contributor.supportsParameter(parameter)) {
templateVariables = contributor.enhance(templateVariables, uriComponents, parameter);
}
}
}

}, builderFactory, getConversionService());
return templateVariables;
}
}

private static Supplier<ConversionService> getConversionService() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
Expand Down Expand Up @@ -184,6 +187,17 @@ void linksToMethodWithPrimaryParam() {
assertThat(link.getHref()).endsWith("/something/foo?a=1&b=2");
}

@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}")
Expand All @@ -208,8 +222,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 {
Expand Down

0 comments on commit 71079bf

Please sign in to comment.