From e6e0618da821bee9807c38e0d938c6b6f04f66dd Mon Sep 17 00:00:00 2001 From: Michael Weirauch Date: Thu, 3 Aug 2017 14:01:07 +0200 Subject: [PATCH] format-only --- .../rest/schemagen/link/LinkCreator.java | 364 +++++++++--------- .../rest/schemagen/link/LinkCreatorTest.java | 72 ++-- 2 files changed, 225 insertions(+), 211 deletions(-) diff --git a/src/main/java/com/mercateo/common/rest/schemagen/link/LinkCreator.java b/src/main/java/com/mercateo/common/rest/schemagen/link/LinkCreator.java index cfc74ce..ad49ddd 100644 --- a/src/main/java/com/mercateo/common/rest/schemagen/link/LinkCreator.java +++ b/src/main/java/com/mercateo/common/rest/schemagen/link/LinkCreator.java @@ -35,185 +35,189 @@ import com.mercateo.common.rest.schemagen.link.relation.Relation; public class LinkCreator { - public static final String TARGET_SCHEMA_PARAM_KEY = "targetSchema"; - - public static final String SCHEMA_PARAM_KEY = "schema"; - - public static final String METHOD_PARAM_KEY = "method"; - - private final JsonSchemaGenerator jsonSchemaGenerator; - - private final LinkFactoryContext linkFactoryContext; - - /** - * @param jsonSchemaGenerator - * @param linkFactoryContext - * - */ - LinkCreator(JsonSchemaGenerator jsonSchemaGenerator, LinkFactoryContext linkFactoryContext) { - this.jsonSchemaGenerator = requireNonNull(jsonSchemaGenerator); - this.linkFactoryContext = linkFactoryContext; - } - - public static Builder setRelation(Relation relation, URI uri) { - requireNonNull(relation); - requireNonNull(uri); - Builder builder = Link.fromUri(uri).rel(relation.getName()); - if (requireNonNull(relation).getType().isShouldBeSerialized()) { - builder.param("relType", relation.getType().getName()); - builder.param("target", relation.getType().getSerializedName()); - } - return builder; - } - - /** - * create a link for a resource method - * - * @param scopes - * list of Scope objects for every scope level - * @param relation - * relation of method - * @return link with schema if applicable - */ - public Link createFor(List scopes, Relation relation) { - return createFor(scopes, relation, requireNonNull(linkFactoryContext)); - } - - /** - * create a link for a resource method - * - * @param scopes - * list of Scope objects for every scope level - * @param relation - * relation of method - * @param linkFactoryContext - * the base URI for resolution of relative URIs and method and - * property checkers - * @return link with schema if applicable - */ - public Link createFor(List scopes, Relation relation, LinkFactoryContext linkFactoryContext) { - final Class resourceClass = scopes.get(0).getInvokedClass(); - UriBuilder uriBuilder = UriBuilder.fromResource(resourceClass); - - Map pathParameters = new HashMap<>(); - for (Scope scope : scopes) { - final Method method = scope.getInvokedMethod(); - final Object[] parameters = scope.getParams(); - if (method.isAnnotationPresent(Path.class)) { - uriBuilder.path(method.getDeclaringClass(), method.getName()); - } - pathParameters.putAll(collectPathParameters(scope, parameters)); - setQueryParameters(uriBuilder, scope, parameters); - } - - URI uri = mergeUri(linkFactoryContext.getBaseUri(), uriBuilder, pathParameters); - - Builder builder = setRelation(relation, uri); - - addLinkProperties(scopes, builder); - - detectMediaType(scopes, builder); - - final Scope lastScopedMethod = Iterables.getLast(scopes); - addHttpMethod(builder, lastScopedMethod); - addSchemaIfNeeded(builder, lastScopedMethod, linkFactoryContext); - return builder.build(); - } - - private URI mergeUri(URI baseUri, UriBuilder uriBuilder, Map pathParameters) { - URI uri = uriBuilder.buildFromMap(pathParameters); - - if (baseUri != null) { - UriBuilder mergedUriBuilder = UriBuilder.fromUri(baseUri); - mergedUriBuilder.path(uri.getPath()); - mergedUriBuilder.replaceQuery(uri.getQuery()); - return mergedUriBuilder.buildFromMap(pathParameters); - } - - return uri; - } - - private void addLinkProperties(List scopes, Builder builder) { - final LinkProperties properties = Iterables.getLast(scopes).getInvokedMethod() - .getAnnotation(LinkProperties.class); - if (properties != null) { - Stream.of(properties.value()).forEach(x -> builder.param(x.key(), x.value())); - } - } - - private void detectMediaType(Collection scopes, Builder builder) { - detectMediaType(Iterables.getLast(scopes).getInvokedMethod()) - .ifPresent(mediatype -> builder.param("mediaType", mediatype)); - } - - private Optional detectMediaType(Method method) { - return Optional.ofNullable(method.getAnnotation(Produces.class)).map(produces -> { - final String[] values = produces.value(); - if (values.length > 0) { - return values[0]; - } - return null; - }); - - } - - private Map collectPathParameters(Scope scope, Object[] parameters) { - final Map pathParameters = new HashMap<>(); - visitAnnotations((parameter, parameterIndex, annotation) -> { - if (annotation instanceof PathParam) { - PathParam pathParamAnnotation = (PathParam) annotation; - pathParameters.put(pathParamAnnotation.value(), parameter); - } else if (annotation instanceof BeanParam) { - BeanParamExtractor beanParamExtractor = new BeanParamExtractor(); - pathParameters.putAll(beanParamExtractor.getPathParameters(parameter)); - } - }, scope.getInvokedMethod(), parameters); - - return pathParameters; - } - - private void setQueryParameters(final UriBuilder uriBuilder, Scope scope, Object[] parameters) { - Type[] realParamTypes = GenericTypeReflector.getExactParameterTypes(scope.getInvokedMethod(), - scope.getInvokedClass()); - visitAnnotations((parameter, parameterIndex, annotation) -> { - if (annotation instanceof QueryParam && parameter != null) { - uriBuilder.queryParam(((QueryParam) annotation).value(), parameter.toString()); - } else if (annotation instanceof BeanParam && parameter != null) { - if (realParamTypes[parameterIndex] instanceof Class) { - BeanParamExtractor beanParamExtractor = new BeanParamExtractor(); - Map queryParameter = beanParamExtractor.getQueryParameters(parameter); - queryParameter.forEach((uriBuilder::queryParam)); - } - } - }, scope.getInvokedMethod(), parameters); - } - - private void addHttpMethod(Builder builder, Scope scope) { - final List> httpMethodAnnotations = Arrays.asList(GET.class, POST.class, PUT.class, - DELETE.class); - final Method invokedMethod = scope.getInvokedMethod(); - final Optional> httpMethod = httpMethodAnnotations.stream() - .filter(invokedMethod::isAnnotationPresent).findFirst(); - - if (httpMethod.isPresent()) { - builder.param(METHOD_PARAM_KEY, httpMethod.get().getSimpleName()); - } else { - throw new IllegalArgumentException("LinkCreator: The method has to be annotated with one of: " - + String.join(", ", (Iterable) httpMethodAnnotations.stream().map(Class::getSimpleName) - .map(m -> '@' + m)::iterator)); - } - } - - private void addSchemaIfNeeded(Builder builder, Scope method, LinkFactoryContext linkFactoryContext) { - Optional optionalInputSchema = jsonSchemaGenerator.createInputSchema(method, - linkFactoryContext.getFieldCheckerForSchema()); - optionalInputSchema.ifPresent(s -> builder.param(SCHEMA_PARAM_KEY, s)); - Optional mt = detectMediaType(method.getInvokedMethod()); - if (mt.isPresent() && MediaType.APPLICATION_JSON.equals(mt.get())) { - Optional optionalOutputSchema = jsonSchemaGenerator.createOutputSchema(method, - linkFactoryContext.getFieldCheckerForSchema()); - optionalOutputSchema.ifPresent(s -> builder.param(TARGET_SCHEMA_PARAM_KEY, s)); - } - } + public static final String TARGET_SCHEMA_PARAM_KEY = "targetSchema"; + + public static final String SCHEMA_PARAM_KEY = "schema"; + + public static final String METHOD_PARAM_KEY = "method"; + + private final JsonSchemaGenerator jsonSchemaGenerator; + + private final LinkFactoryContext linkFactoryContext; + + /** + * @param jsonSchemaGenerator + * @param linkFactoryContext + * + */ + LinkCreator(JsonSchemaGenerator jsonSchemaGenerator, LinkFactoryContext linkFactoryContext) { + this.jsonSchemaGenerator = requireNonNull(jsonSchemaGenerator); + this.linkFactoryContext = linkFactoryContext; + } + + public static Builder setRelation(Relation relation, URI uri) { + requireNonNull(relation); + requireNonNull(uri); + Builder builder = Link.fromUri(uri).rel(relation.getName()); + if (requireNonNull(relation).getType().isShouldBeSerialized()) { + builder.param("relType", relation.getType().getName()); + builder.param("target", relation.getType().getSerializedName()); + } + return builder; + } + + /** + * create a link for a resource method + * + * @param scopes + * list of Scope objects for every scope level + * @param relation + * relation of method + * @return link with schema if applicable + */ + public Link createFor(List scopes, Relation relation) { + return createFor(scopes, relation, requireNonNull(linkFactoryContext)); + } + + /** + * create a link for a resource method + * + * @param scopes + * list of Scope objects for every scope level + * @param relation + * relation of method + * @param linkFactoryContext + * the base URI for resolution of relative URIs and method and + * property checkers + * @return link with schema if applicable + */ + public Link createFor(List scopes, Relation relation, + LinkFactoryContext linkFactoryContext) { + final Class resourceClass = scopes.get(0).getInvokedClass(); + UriBuilder uriBuilder = UriBuilder.fromResource(resourceClass); + + Map pathParameters = new HashMap<>(); + for (Scope scope : scopes) { + final Method method = scope.getInvokedMethod(); + final Object[] parameters = scope.getParams(); + if (method.isAnnotationPresent(Path.class)) { + uriBuilder.path(method.getDeclaringClass(), method.getName()); + } + pathParameters.putAll(collectPathParameters(scope, parameters)); + setQueryParameters(uriBuilder, scope, parameters); + } + + URI uri = mergeUri(linkFactoryContext.getBaseUri(), uriBuilder, pathParameters); + + Builder builder = setRelation(relation, uri); + + addLinkProperties(scopes, builder); + + detectMediaType(scopes, builder); + + final Scope lastScopedMethod = Iterables.getLast(scopes); + addHttpMethod(builder, lastScopedMethod); + addSchemaIfNeeded(builder, lastScopedMethod, linkFactoryContext); + return builder.build(); + } + + private URI mergeUri(URI baseUri, UriBuilder uriBuilder, Map pathParameters) { + URI uri = uriBuilder.buildFromMap(pathParameters); + + if (baseUri != null) { + UriBuilder mergedUriBuilder = UriBuilder.fromUri(baseUri); + mergedUriBuilder.path(uri.getPath()); + mergedUriBuilder.replaceQuery(uri.getQuery()); + return mergedUriBuilder.buildFromMap(pathParameters); + } + + return uri; + } + + private void addLinkProperties(List scopes, Builder builder) { + final LinkProperties properties = Iterables.getLast(scopes).getInvokedMethod() + .getAnnotation(LinkProperties.class); + if (properties != null) { + Stream.of(properties.value()).forEach(x -> builder.param(x.key(), x.value())); + } + } + + private void detectMediaType(Collection scopes, Builder builder) { + detectMediaType(Iterables.getLast(scopes).getInvokedMethod()).ifPresent(mediatype -> builder + .param("mediaType", mediatype)); + } + + private Optional detectMediaType(Method method) { + return Optional.ofNullable(method.getAnnotation(Produces.class)).map(produces -> { + final String[] values = produces.value(); + if (values.length > 0) { + return values[0]; + } + return null; + }); + + } + + private Map collectPathParameters(Scope scope, Object[] parameters) { + final Map pathParameters = new HashMap<>(); + visitAnnotations((parameter, parameterIndex, annotation) -> { + if (annotation instanceof PathParam) { + PathParam pathParamAnnotation = (PathParam) annotation; + pathParameters.put(pathParamAnnotation.value(), parameter); + } else if (annotation instanceof BeanParam) { + BeanParamExtractor beanParamExtractor = new BeanParamExtractor(); + pathParameters.putAll(beanParamExtractor.getPathParameters(parameter)); + } + }, scope.getInvokedMethod(), parameters); + + return pathParameters; + } + + private void setQueryParameters(final UriBuilder uriBuilder, Scope scope, Object[] parameters) { + Type[] realParamTypes = GenericTypeReflector.getExactParameterTypes(scope + .getInvokedMethod(), scope.getInvokedClass()); + visitAnnotations((parameter, parameterIndex, annotation) -> { + if (annotation instanceof QueryParam && parameter != null) { + uriBuilder.queryParam(((QueryParam) annotation).value(), parameter.toString()); + } else if (annotation instanceof BeanParam && parameter != null) { + if (realParamTypes[parameterIndex] instanceof Class) { + BeanParamExtractor beanParamExtractor = new BeanParamExtractor(); + Map queryParameter = beanParamExtractor.getQueryParameters( + parameter); + queryParameter.forEach((uriBuilder::queryParam)); + } + } + }, scope.getInvokedMethod(), parameters); + } + + private void addHttpMethod(Builder builder, Scope scope) { + final List> httpMethodAnnotations = Arrays.asList(GET.class, + POST.class, PUT.class, DELETE.class); + final Method invokedMethod = scope.getInvokedMethod(); + final Optional> httpMethod = httpMethodAnnotations.stream() + .filter(invokedMethod::isAnnotationPresent).findFirst(); + + if (httpMethod.isPresent()) { + builder.param(METHOD_PARAM_KEY, httpMethod.get().getSimpleName()); + } else { + throw new IllegalArgumentException( + "LinkCreator: The method has to be annotated with one of: " + String.join(", ", + (Iterable) httpMethodAnnotations.stream().map( + Class::getSimpleName).map(m -> '@' + m)::iterator)); + } + } + + private void addSchemaIfNeeded(Builder builder, Scope method, + LinkFactoryContext linkFactoryContext) { + Optional optionalInputSchema = jsonSchemaGenerator.createInputSchema(method, + linkFactoryContext.getFieldCheckerForSchema()); + optionalInputSchema.ifPresent(s -> builder.param(SCHEMA_PARAM_KEY, s)); + Optional mt = detectMediaType(method.getInvokedMethod()); + if (mt.isPresent() && MediaType.APPLICATION_JSON.equals(mt.get())) { + Optional optionalOutputSchema = jsonSchemaGenerator.createOutputSchema(method, + linkFactoryContext.getFieldCheckerForSchema()); + optionalOutputSchema.ifPresent(s -> builder.param(TARGET_SCHEMA_PARAM_KEY, s)); + } + } } diff --git a/src/test/java/com/mercateo/common/rest/schemagen/link/LinkCreatorTest.java b/src/test/java/com/mercateo/common/rest/schemagen/link/LinkCreatorTest.java index e1e50e8..1b6ba72 100644 --- a/src/test/java/com/mercateo/common/rest/schemagen/link/LinkCreatorTest.java +++ b/src/test/java/com/mercateo/common/rest/schemagen/link/LinkCreatorTest.java @@ -34,8 +34,8 @@ public class LinkCreatorTest { @Test public void testGET() throws NoSuchMethodException, SecurityException { - Link link = createFor(ResourceClass.class, ResourceClass.class.getMethod("getSomething", String.class), Relation - .of(Rel.SELF), "12"); + Link link = createFor(ResourceClass.class, ResourceClass.class.getMethod("getSomething", + String.class), Relation.of(Rel.SELF), "12"); assertEquals("http://host/base/resource/method/12", link.getUri().toString()); assertEquals("GET", link.getParams().get("method")); @@ -44,14 +44,17 @@ public void testGET() throws NoSuchMethodException, SecurityException { @Test public void testWithoutBasePath() throws NoSuchMethodException, SecurityException { - final CallScope callScope = new CallScope(ResourceClass.class, ResourceClass.class.getMethod("getSomething", String.class), new Object[]{"12"}, null); + final CallScope callScope = new CallScope(ResourceClass.class, ResourceClass.class + .getMethod("getSomething", String.class), new Object[] { "12" }, null); final JsonSchemaGenerator jsonSchemaGenerator = createJsonSchemaGenerator(); - final LinkFactoryContext linkFactoryContext = new LinkFactoryContextDefault(null, o -> true, (o, c) -> true); + final LinkFactoryContext linkFactoryContext = new LinkFactoryContextDefault(null, o -> true, + (o, c) -> true); final LinkCreator linkCreator = new LinkCreator(jsonSchemaGenerator, linkFactoryContext); - final Link link = linkCreator.createFor(Collections.singletonList(callScope), Relation.of(Rel.SELF)); + final Link link = linkCreator.createFor(Collections.singletonList(callScope), Relation.of( + Rel.SELF)); assertEquals("resource/method/12", link.getUri().toString()); assertEquals("GET", link.getParams().get("method")); @@ -60,8 +63,8 @@ public void testWithoutBasePath() throws NoSuchMethodException, SecurityExceptio @Test public void testPOST() throws NoSuchMethodException, SecurityException { - Link link = createFor(ResourceClass.class, ResourceClass.class.getMethod("postSomething", Something.class), - Relation.of(Rel.SELF)); + Link link = createFor(ResourceClass.class, ResourceClass.class.getMethod("postSomething", + Something.class), Relation.of(Rel.SELF)); assertEquals("http://host/base/resource/method", link.getUri().toString()); assertEquals("POST", link.getParams().get("method")); @@ -71,8 +74,8 @@ public void testPOST() throws NoSuchMethodException, SecurityException { @Test public void testPOSTWithBaseURI() throws NoSuchMethodException, SecurityException { - Link link = createFor(ResourceClass.class, ResourceClass.class.getMethod("postSomething", Something.class), - Relation.of(Rel.SELF)); + Link link = createFor(ResourceClass.class, ResourceClass.class.getMethod("postSomething", + Something.class), Relation.of(Rel.SELF)); assertEquals("http://host/base/resource/method", link.getUri().toString()); assertEquals("POST", link.getParams().get("method")); @@ -81,8 +84,8 @@ public void testPOSTWithBaseURI() throws NoSuchMethodException, SecurityExceptio @Test public void failsIfHttpMethodIsMissing() throws NoSuchMethodException, SecurityException { - assertThatThrownBy(() -> createFor(ResourceClass.class, ResourceClass.class.getMethod("noHttpMethod"), Relation - .of(Rel.SELF))) // + assertThatThrownBy(() -> createFor(ResourceClass.class, ResourceClass.class.getMethod( + "noHttpMethod"), Relation.of(Rel.SELF))) // .isInstanceOf(IllegalArgumentException.class) // .hasMessage( "LinkCreator: The method has to be annotated with one of: @GET, @POST, @PUT, @DELETE"); @@ -91,7 +94,8 @@ public void failsIfHttpMethodIsMissing() throws NoSuchMethodException, SecurityE @Test public void testBeanParams() throws NoSuchMethodException, SecurityException { @Path("test") - class ImplementedGenricResource extends GenericResource { + class ImplementedGenricResource extends + GenericResource { @Override protected Something getReturnType(ImplementedBeanParamType param) { return new Something(); @@ -102,8 +106,8 @@ protected Something getReturnType(ImplementedBeanParamType param) { implementedBeanParamType.setPathParam("path"); implementedBeanParamType.setQueryParam1("v1"); implementedBeanParamType.setQueryParam2("v2"); - Scope scope = new CallScope(ImplementedGenricResource.class, ImplementedGenricResource.class.getMethod("get", - Object.class), new Object[] { implementedBeanParamType }, null); + Scope scope = new CallScope(ImplementedGenricResource.class, ImplementedGenricResource.class + .getMethod("get", Object.class), new Object[] { implementedBeanParamType }, null); Link link = createFor(scope, Relation.of(Rel.SELF)); @@ -123,8 +127,8 @@ public String get(@PathParam("pathParam1") String param) { } } - Scope scope = new CallScope(ImplementedGenricResource.class, ImplementedGenricResource.class.getMethod("get", - String.class), new String[] { "foo" }, null); + Scope scope = new CallScope(ImplementedGenricResource.class, ImplementedGenricResource.class + .getMethod("get", String.class), new String[] { "foo" }, null); Link link = createFor(scope, Relation.of(Rel.SELF)); @@ -136,7 +140,8 @@ public String get(@PathParam("pathParam1") String param) { public void testBeanParamsWithDefaultValues() throws NoSuchMethodException, SecurityException { @Path("test") - class ImplementedGenricResource extends GenericResource { + class ImplementedGenricResource extends + GenericResource { @Override protected Something getReturnType(ImplementedBeanParamType param) { return new Something(); @@ -146,8 +151,8 @@ protected Something getReturnType(ImplementedBeanParamType param) { ImplementedBeanParamType implementedBeanParamType = new ImplementedBeanParamType(); implementedBeanParamType.setPathParam("path"); - Scope scope = new CallScope(ImplementedGenricResource.class, ImplementedGenricResource.class.getMethod("get", - Object.class), new Object[] { implementedBeanParamType }, null); + Scope scope = new CallScope(ImplementedGenricResource.class, ImplementedGenricResource.class + .getMethod("get", Object.class), new Object[] { implementedBeanParamType }, null); Link link = createFor(scope, Relation.of(Rel.SELF)); @@ -156,10 +161,12 @@ protected Something getReturnType(ImplementedBeanParamType param) { } @Test - public void testBeanParamWithMultiValueQueryParam() throws NoSuchMethodException, SecurityException { + public void testBeanParamWithMultiValueQueryParam() throws NoSuchMethodException, + SecurityException { @Path("test") - class ImplementedGenericResource extends GenericResource { + class ImplementedGenericResource extends + GenericResource { @Override protected Something getReturnType(ImplementedBeanParamType param) { return new Something(); @@ -169,27 +176,29 @@ protected Something getReturnType(ImplementedBeanParamType param) { ImplementedBeanParamType implementedBeanParamType = new ImplementedBeanParamType(); implementedBeanParamType.setPathParam("path"); implementedBeanParamType.setElements("foo", "bar", "baz"); - Scope scope = new CallScope(ImplementedGenericResource.class, ImplementedGenericResource.class.getMethod("get", - Object.class), new Object[] { implementedBeanParamType }, null); + Scope scope = new CallScope(ImplementedGenericResource.class, + ImplementedGenericResource.class.getMethod("get", Object.class), new Object[] { + implementedBeanParamType }, null); Link link = createFor(scope, Relation.of(Rel.SELF)); assertEquals("http://host/base/test/path?elements=foo&elements=bar&elements=baz", link - .getUri() - .toString()); + .getUri().toString()); assertEquals("GET", link.getParams().get("method")); assertEquals("application/json", link.getParams().get("mediaType")); } private JsonSchemaGenerator createJsonSchemaGenerator() { JsonSchemaGenerator jsonSchemaGenerator = Mockito.mock(JsonSchemaGenerator.class); - when(jsonSchemaGenerator.createInputSchema(Matchers.any(), Matchers.any())).thenReturn(Optional.of(testSchema)); - when(jsonSchemaGenerator.createOutputSchema(Matchers.any(), Matchers.any())).thenReturn(Optional.of( - testSchema)); + when(jsonSchemaGenerator.createInputSchema(Matchers.any(), Matchers.any())).thenReturn( + Optional.of(testSchema)); + when(jsonSchemaGenerator.createOutputSchema(Matchers.any(), Matchers.any())).thenReturn( + Optional.of(testSchema)); return jsonSchemaGenerator; } - private Link createFor(Class invokedClass, Method method, Relation relation, Object... params) { + private Link createFor(Class invokedClass, Method method, Relation relation, + Object... params) { return createFor(new CallScope(invokedClass, method, params, null), relation); } @@ -198,9 +207,10 @@ private Link createFor(Scope method, Relation relation) { final LinkFactoryContext linkFactoryContext = new LinkFactoryContextDefault(URI.create( "http://host/base/"), o -> true, (o, c) -> true); - final LinkCreator linkCreator = new LinkCreator(jsonSchemaGenerator,null); + final LinkCreator linkCreator = new LinkCreator(jsonSchemaGenerator, null); - return linkCreator.createFor(Collections.singletonList(method), relation, linkFactoryContext); + return linkCreator.createFor(Collections.singletonList(method), relation, + linkFactoryContext); } } \ No newline at end of file