-
Notifications
You must be signed in to change notification settings - Fork 475
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#482 - Add support for Collection+JSON mediatype.
Introduce support for media type application/vnd.collection+json. Collection+JSON doesn't allow metadata at the top, so paging data can't be covered, however, everything else fits. Also moved a little bit more into Affordance and SpringMvcAffordance to avoid using Spring MVC annotations directly in a given mediatype's AffordanceModel. Refactored bits of HAL-FORMS to reuse the new PropertyUtils, ensuring Jackson ignore annotations are taken into consideration. Also added MockMVC tests to show HAL-FORMS and Collection+JSON working together, against the same controller.
- Loading branch information
Showing
74 changed files
with
4,633 additions
and
205 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
image:https://spring.io/badges/spring-hateoas/ga.svg[http://projects.spring.io/spring-hateoas/#quick-start] | ||
image:https://spring.io/badges/spring-hateoas/snapshot.svg[http://projects.spring.io/spring-hateoas/#quick-start] | ||
|
||
= Spring HATEOAS | ||
|
||
This project provides some APIs to ease creating REST representations that follow the http://en.wikipedia.org/wiki/HATEOAS[HATEOAS] principle when working with Spring and especially Spring MVC. The core problem it tries to address is link creation and representation assembly. | ||
|
||
== Working with Spring HATEOAS | ||
|
||
Since all commits are headlined with its github issue, git will treat it as a comment. To get around this, apply the following configuration to your clone: | ||
|
||
[source] | ||
---- | ||
git config core.commentchar "/" | ||
---- | ||
|
||
== Resources | ||
|
||
* Reference documentation - http://docs.spring.io/spring-hateoas/docs/current/reference/html/[html], http://docs.spring.io/spring-hateoas/docs/current/reference/pdf/spring-hateoas-reference.pdf[pdf] | ||
* http://docs.spring.io/spring-hateoas/docs/current-SNAPSHOT/api/[JavaDoc] | ||
* https://spring.io/guides/gs/rest-hateoas/[Getting started guide] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/main/java/org/springframework/hateoas/QueryParameter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright 2018 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 | ||
* | ||
* http://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; | ||
|
||
import lombok.Data; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
/** | ||
* Web framework-neutral representation of a web request's query parameter (http://example.com?name=foo). | ||
* | ||
* @author Greg Turnquist | ||
*/ | ||
@Data | ||
@RequiredArgsConstructor | ||
public class QueryParameter { | ||
|
||
private final String name; | ||
private final boolean required; | ||
private final String value; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
src/main/java/org/springframework/hateoas/collectionjson/CollectionJson.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* Copyright 2015 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 | ||
* | ||
* http://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.collectionjson; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.Value; | ||
import lombok.experimental.Wither; | ||
|
||
import java.util.List; | ||
|
||
import org.springframework.hateoas.Link; | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonInclude; | ||
import com.fasterxml.jackson.annotation.JsonInclude.Include; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
/** | ||
* Representation of the "collection" part of a Collection+JSON document. | ||
* | ||
* @author Greg Turnquist | ||
*/ | ||
@Value | ||
@Wither(AccessLevel.PACKAGE) | ||
class CollectionJson<T> { | ||
|
||
private String version; | ||
private String href; | ||
|
||
@JsonInclude(Include.NON_EMPTY) | ||
private List<Link> links; | ||
|
||
@JsonInclude(Include.NON_EMPTY) | ||
private List<CollectionJsonItem<T>> items; | ||
|
||
@JsonInclude(Include.NON_EMPTY) | ||
private List<CollectionJsonQuery> queries; | ||
|
||
@JsonInclude(Include.NON_NULL) | ||
private CollectionJsonTemplate template; | ||
|
||
@JsonInclude(Include.NON_NULL) | ||
private CollectionJsonError error; | ||
|
||
@JsonCreator | ||
CollectionJson(@JsonProperty("version") String version, @JsonProperty("href") String href, | ||
@JsonProperty("links") List<Link> links, @JsonProperty("items") List<CollectionJsonItem<T>> items, | ||
@JsonProperty("queries") List<CollectionJsonQuery> queries, | ||
@JsonProperty("template") CollectionJsonTemplate template, | ||
@JsonProperty("error") CollectionJsonError error) { | ||
|
||
this.version = version; | ||
this.href = href; | ||
this.links = links; | ||
this.items = items; | ||
this.queries = queries; | ||
this.template = template; | ||
this.error = error; | ||
} | ||
|
||
CollectionJson() { | ||
this("1.0", null, null, null, null, null, null); | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
src/main/java/org/springframework/hateoas/collectionjson/CollectionJsonAffordanceModel.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* | ||
* Copyright 2018 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 | ||
* | ||
* http://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.collectionjson; | ||
|
||
import lombok.Getter; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
import org.springframework.core.ResolvableType; | ||
import org.springframework.hateoas.Affordance; | ||
import org.springframework.hateoas.AffordanceModel; | ||
import org.springframework.hateoas.MediaTypes; | ||
import org.springframework.hateoas.QueryParameter; | ||
import org.springframework.hateoas.support.PropertyUtils; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.web.util.UriComponents; | ||
|
||
/** | ||
* @author Greg Turnquist | ||
*/ | ||
class CollectionJsonAffordanceModel implements AffordanceModel { | ||
|
||
private static final List<HttpMethod> METHODS_FOR_INPUT_DETECTION = Arrays.asList(HttpMethod.POST, HttpMethod.PUT, | ||
HttpMethod.PATCH); | ||
|
||
private final Affordance affordance; | ||
private final UriComponents components; | ||
private final @Getter List<CollectionJsonData> inputProperties; | ||
private final @Getter List<CollectionJsonData> queryProperties; | ||
|
||
CollectionJsonAffordanceModel(Affordance affordance, UriComponents components) { | ||
|
||
this.affordance = affordance; | ||
this.components = components; | ||
|
||
this.inputProperties = determineAffordanceInputs(); | ||
this.queryProperties = determineQueryProperties(); | ||
} | ||
|
||
@Override | ||
public Collection<MediaType> getMediaTypes() { | ||
return Collections.singleton(MediaTypes.COLLECTION_JSON); | ||
} | ||
|
||
public String getRel() { | ||
return isHttpGetMethod() ? this.affordance.getName() : ""; | ||
} | ||
|
||
public String getUri() { | ||
return isHttpGetMethod() ? this.components.toUriString() : ""; | ||
} | ||
|
||
/** | ||
* Transform a list of {@link QueryParameter}s into a list of {@link CollectionJsonData} objects. | ||
* | ||
* @return | ||
*/ | ||
private List<CollectionJsonData> determineQueryProperties() { | ||
|
||
if (!isHttpGetMethod()) { | ||
return Collections.emptyList(); | ||
} | ||
|
||
return this.affordance.getQueryMethodParameters().stream() | ||
.map(queryProperty -> new CollectionJsonData().withName(queryProperty.getName()).withValue("")) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
private boolean isHttpGetMethod() { | ||
return this.affordance.getHttpMethod().equals(HttpMethod.GET); | ||
} | ||
|
||
/** | ||
* Look at the inputs for a Spring MVC controller method to decide the {@link Affordance}'s properties. | ||
* Then transform them into a list of {@link CollectionJsonData} objects. | ||
*/ | ||
private List<CollectionJsonData> determineAffordanceInputs() { | ||
|
||
if (!METHODS_FOR_INPUT_DETECTION.contains(affordance.getHttpMethod())) { | ||
return Collections.emptyList(); | ||
} | ||
|
||
return this.affordance.getInputMethodParameters().stream() | ||
.findFirst() | ||
.map(methodParameter -> { | ||
ResolvableType resolvableType = ResolvableType.forMethodParameter(methodParameter); | ||
return PropertyUtils.findProperties(resolvableType); | ||
}) | ||
.orElse(Collections.emptyList()) | ||
.stream() | ||
.map(property -> new CollectionJsonData().withName(property).withValue("")) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
...java/org/springframework/hateoas/collectionjson/CollectionJsonAffordanceModelFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright 2018 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 | ||
* | ||
* http://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.collectionjson; | ||
|
||
import lombok.Getter; | ||
|
||
import org.springframework.hateoas.Affordance; | ||
import org.springframework.hateoas.AffordanceModel; | ||
import org.springframework.hateoas.MediaTypes; | ||
import org.springframework.hateoas.core.AffordanceModelFactory; | ||
import org.springframework.hateoas.core.DummyInvocationUtils.MethodInvocation; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.web.util.UriComponents; | ||
|
||
/** | ||
* @author Greg Turnquist | ||
*/ | ||
class CollectionJsonAffordanceModelFactory implements AffordanceModelFactory { | ||
|
||
private final @Getter MediaType mediaType = MediaTypes.COLLECTION_JSON; | ||
|
||
/** | ||
* Look up the {@link AffordanceModel} for this factory. | ||
* | ||
* @param affordance | ||
* @param invocationValue | ||
* @param components | ||
* @return | ||
*/ | ||
@Override | ||
public AffordanceModel getAffordanceModel(Affordance affordance, MethodInvocation invocationValue, UriComponents components) { | ||
return new CollectionJsonAffordanceModel(affordance, components); | ||
} | ||
} |
Oops, something went wrong.