#1038 - Update documentation and testing for ALPS.
Also make the ALPS types support deserialization using @JsonCreator-based private constructor calls.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
= Media types
|
||||
:code-dir: ../../../src/docs/java/org/springframework/hateoas
|
||||
:resource-dir: ../../../src/docs/resources/org/springframework/hateoas
|
||||
:test-dir: ../../../src/test/java/org/springframework/hateoas
|
||||
:linkattrs:
|
||||
|
||||
[[mediatypes.hal]]
|
||||
@@ -408,6 +409,86 @@ https://github.com/spring-projects/spring-hateoas/issues[open a ticket, window="
|
||||
|
||||
NOTE: *UBER media type* is not associated in any way with *Uber Technologies Inc.*, the ride sharing company.
|
||||
|
||||
[[mediatypes.alps]]
|
||||
== ALPS - Application-Level Profile Semantics
|
||||
|
||||
https://tools.ietf.org/html/draft-amundsen-richardson-foster-alps-01[ALPS, window="_blank"] is a media type for providing
|
||||
profile-based metadata about another resource.
|
||||
|
||||
[quote, Mike Amundsen, ALPS spec]
|
||||
____
|
||||
An ALPS document can be used as a profile to
|
||||
explain the application semantics of a document with an application-
|
||||
agnostic media type (such as HTML, HAL, Collection+JSON, Siren,
|
||||
etc.). This increases the reusability of profile documents across
|
||||
media types.
|
||||
____
|
||||
|
||||
ALPS requires no special activation. Instead you "build" an `Alps` record and return it from either a Spring MVC or a Spring WebFlux web method as shown below:
|
||||
|
||||
.Building an `Alps` record
|
||||
====
|
||||
[source, java, tabsize=2, indent=0]
|
||||
----
|
||||
include::{test-dir}/support/WebMvcEmployeeController.java[tag=alps-profile]
|
||||
----
|
||||
* This example leverages `PropertyUtils.getExposedProperties()` to extract metadata about the domain object's attributes.
|
||||
====
|
||||
|
||||
This fragment has test data plugged in. It yields JSON like this:
|
||||
|
||||
.ALPS JSON
|
||||
====
|
||||
----
|
||||
{
|
||||
"version": "1.0",
|
||||
"doc": {
|
||||
"format": "TEXT",
|
||||
"href": "https://example.org/samples/full/doc.html",
|
||||
"value": "value goes here"
|
||||
},
|
||||
"descriptor": [
|
||||
{
|
||||
"id": "class field [name]",
|
||||
"name": "name",
|
||||
"type": "SEMANTIC",
|
||||
"descriptor": [
|
||||
{
|
||||
"id": "embedded"
|
||||
}
|
||||
],
|
||||
"ext": {
|
||||
"id": "ext [name]",
|
||||
"href": "https://example.org/samples/ext/name",
|
||||
"value": "value goes here"
|
||||
},
|
||||
"rt": "rt for [name]"
|
||||
},
|
||||
{
|
||||
"id": "class field [role]",
|
||||
"name": "role",
|
||||
"type": "SEMANTIC",
|
||||
"descriptor": [
|
||||
{
|
||||
"id": "embedded"
|
||||
}
|
||||
],
|
||||
"ext": {
|
||||
"id": "ext [role]",
|
||||
"href": "https://example.org/samples/ext/role",
|
||||
"value": "value goes here"
|
||||
},
|
||||
"rt": "rt for [role]"
|
||||
}
|
||||
]
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Instead of linking each field "automatically" to a domain object's fields, you can write them by hand if you like. It's also possible
|
||||
to use Spring Framework's message bundles and the `MessageSource` interface. This gives you the ability to delegate these values to
|
||||
locale-specific message bundles and even internationalize the metadata.
|
||||
|
||||
[[mediatypes.custom]]
|
||||
== Registering a custom media type
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ import org.springframework.hateoas.mediatype.alps.Descriptor.DescriptorBuilder;
|
||||
import org.springframework.hateoas.mediatype.alps.Doc.DocBuilder;
|
||||
import org.springframework.hateoas.mediatype.alps.Ext.ExtBuilder;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
/**
|
||||
@@ -38,14 +40,23 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
*/
|
||||
@Value
|
||||
@Builder(builderMethodName = "alps")
|
||||
@JsonPropertyOrder({"version", "doc", "descriptor"})
|
||||
@JsonPropertyOrder({ "version", "doc", "descriptor" })
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Alps {
|
||||
|
||||
private final String version = "1.0";
|
||||
private final String version;
|
||||
private final Doc doc;
|
||||
private final List<Descriptor> descriptor;
|
||||
|
||||
@JsonCreator
|
||||
private Alps(@JsonProperty("version") String version, @JsonProperty("doc") Doc doc,
|
||||
@JsonProperty("descriptor") List<Descriptor> descriptor) {
|
||||
|
||||
this.version = "1.0";
|
||||
this.doc = doc;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link DescriptorBuilder}.
|
||||
*
|
||||
|
||||
@@ -20,7 +20,9 @@ import lombok.Value;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
/**
|
||||
@@ -33,14 +35,32 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
*/
|
||||
@Value
|
||||
@Builder
|
||||
@JsonPropertyOrder({"id", "href", "name", "type", "doc", "descriptor", "ext"})
|
||||
@JsonPropertyOrder({ "id", "href", "name", "type", "doc", "descriptor", "ext" })
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Descriptor {
|
||||
|
||||
private final String id, href, name;
|
||||
private final String id;
|
||||
private final String href;
|
||||
private final String name;
|
||||
private final Doc doc;
|
||||
private final Type type;
|
||||
private final Ext ext;
|
||||
private final String rt;
|
||||
private final List<Descriptor> descriptor;
|
||||
|
||||
@JsonCreator
|
||||
private Descriptor(@JsonProperty("id") String id, @JsonProperty("href") String href,
|
||||
@JsonProperty("name") String name, @JsonProperty("doc") Doc doc, @JsonProperty("type") Type type,
|
||||
@JsonProperty("ext") Ext ext, @JsonProperty("rt") String rt,
|
||||
@JsonProperty("descriptor") List<Descriptor> descriptor) {
|
||||
|
||||
this.id = id;
|
||||
this.href = href;
|
||||
this.name = name;
|
||||
this.doc = doc;
|
||||
this.type = type;
|
||||
this.ext = ext;
|
||||
this.rt = rt;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.alps;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
/**
|
||||
@@ -34,12 +35,12 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
*/
|
||||
@Value
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@JsonPropertyOrder({"format", "href", "value"})
|
||||
@JsonPropertyOrder({ "format", "href", "value" })
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Doc {
|
||||
|
||||
private final String href, value;
|
||||
private final String href;
|
||||
private final String value;
|
||||
private final Format format;
|
||||
|
||||
/**
|
||||
@@ -57,4 +58,13 @@ public class Doc {
|
||||
this.value = value;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
private Doc(@JsonProperty("href") String href, @JsonProperty("value") String value,
|
||||
@JsonProperty("format") Format format) {
|
||||
|
||||
this.href = href;
|
||||
this.value = value;
|
||||
this.format = format;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.hateoas.mediatype.alps;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
/**
|
||||
@@ -30,10 +32,18 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
*/
|
||||
@Value
|
||||
@Builder
|
||||
@JsonPropertyOrder({"id", "href", "value"})
|
||||
@JsonPropertyOrder({ "id", "href", "value" })
|
||||
public class Ext {
|
||||
|
||||
private final String id;
|
||||
private final String href;
|
||||
private final String value;
|
||||
|
||||
@JsonCreator
|
||||
private Ext(@JsonProperty("id") String id, @JsonProperty("href") String href, @JsonProperty("value") String value) {
|
||||
|
||||
this.id = id;
|
||||
this.href = href;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2019 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.mediatype.alps;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType.*;
|
||||
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
||||
import org.springframework.hateoas.config.WebClientConfigurer;
|
||||
import org.springframework.hateoas.support.WebFluxEmployeeController;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
public class AlpsWebFluxIntegrationTest {
|
||||
|
||||
@Autowired WebTestClient testClient;
|
||||
|
||||
@Test
|
||||
void profileEndpointReturnsAlps() {
|
||||
|
||||
this.testClient.get().uri("/profile") //
|
||||
.accept(MediaTypes.ALPS_JSON) //
|
||||
.exchange() //
|
||||
.expectStatus().isOk() //
|
||||
.returnResult(Alps.class).getResponseBody() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextMatches(alps -> {
|
||||
|
||||
assertThat(alps.getVersion()).isEqualTo("1.0");
|
||||
assertThat(alps.getDoc().getFormat()).isEqualByComparingTo(Format.TEXT);
|
||||
assertThat(alps.getDoc().getHref()).isEqualTo("https://example.org/samples/full/doc.html");
|
||||
assertThat(alps.getDoc().getValue()).isEqualTo("value goes here");
|
||||
assertThat(alps.getDescriptor()).hasSize(2);
|
||||
|
||||
assertThat(alps.getDescriptor().get(0).getId()).isEqualTo("class field [name]");
|
||||
assertThat(alps.getDescriptor().get(0).getName()).isEqualTo("name");
|
||||
assertThat(alps.getDescriptor().get(0).getType()).isEqualByComparingTo(Type.SEMANTIC);
|
||||
assertThat(alps.getDescriptor().get(0).getDescriptor()).hasSize(1);
|
||||
assertThat(alps.getDescriptor().get(0).getDescriptor().get(0).getId()).isEqualTo("embedded");
|
||||
assertThat(alps.getDescriptor().get(0).getExt().getId()).isEqualTo("ext [name]");
|
||||
assertThat(alps.getDescriptor().get(0).getExt().getHref()).isEqualTo("https://example.org/samples/ext/name");
|
||||
assertThat(alps.getDescriptor().get(0).getExt().getValue()).isEqualTo("value goes here");
|
||||
assertThat(alps.getDescriptor().get(0).getRt()).isEqualTo("rt for [name]");
|
||||
|
||||
assertThat(alps.getDescriptor().get(1).getId()).isEqualTo("class field [role]");
|
||||
assertThat(alps.getDescriptor().get(1).getName()).isEqualTo("role");
|
||||
assertThat(alps.getDescriptor().get(1).getType()).isEqualByComparingTo(Type.SEMANTIC);
|
||||
assertThat(alps.getDescriptor().get(1).getDescriptor()).hasSize(1);
|
||||
assertThat(alps.getDescriptor().get(1).getDescriptor().get(0).getId()).isEqualTo("embedded");
|
||||
assertThat(alps.getDescriptor().get(1).getExt().getId()).isEqualTo("ext [role]");
|
||||
assertThat(alps.getDescriptor().get(1).getExt().getHref()).isEqualTo("https://example.org/samples/ext/role");
|
||||
assertThat(alps.getDescriptor().get(1).getExt().getValue()).isEqualTo("value goes here");
|
||||
assertThat(alps.getDescriptor().get(1).getRt()).isEqualTo("rt for [role]");
|
||||
|
||||
return true;
|
||||
}) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
@EnableHypermediaSupport(type = HAL)
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
WebFluxEmployeeController employeeController() {
|
||||
return new WebFluxEmployeeController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebTestClient webTestClient(WebClientConfigurer webClientConfigurer, ApplicationContext ctx) {
|
||||
|
||||
return WebTestClient.bindToApplicationContext(ctx).build() //
|
||||
.mutate() //
|
||||
.exchangeStrategies(webClientConfigurer.hypermediaExchangeStrategies()) //
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2019 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.mediatype.alps;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
||||
import org.springframework.hateoas.support.WebMvcEmployeeController;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class AlpsWebMvcIntegrationTest {
|
||||
|
||||
@Autowired WebApplicationContext context;
|
||||
|
||||
MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.mockMvc = webAppContextSetup(this.context).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void profileEndpointReturnsAlps() throws Exception {
|
||||
|
||||
this.mockMvc.perform(get("/profile").accept(MediaTypes.ALPS_JSON_VALUE)) //
|
||||
.andDo(print()) //
|
||||
.andExpect(status().isOk()) //
|
||||
.andExpect(jsonPath("$.version", is("1.0"))) //
|
||||
.andExpect(jsonPath("$.doc.format", is("TEXT")))
|
||||
.andExpect(jsonPath("$.doc.href", is("https://example.org/samples/full/doc.html")))
|
||||
.andExpect(jsonPath("$.doc.value", is("value goes here"))).andExpect(jsonPath("$.descriptor", hasSize(2)))
|
||||
|
||||
.andExpect(jsonPath("$.descriptor[0].id", is("class field [name]")))
|
||||
.andExpect(jsonPath("$.descriptor[0].name", is("name")))
|
||||
.andExpect(jsonPath("$.descriptor[0].type", is("SEMANTIC")))
|
||||
.andExpect(jsonPath("$.descriptor[0].descriptor", hasSize(1)))
|
||||
.andExpect(jsonPath("$.descriptor[0].descriptor[0].id", is("embedded")))
|
||||
.andExpect(jsonPath("$.descriptor[0].ext.id", is("ext [name]")))
|
||||
.andExpect(jsonPath("$.descriptor[0].ext.href", is("https://example.org/samples/ext/name")))
|
||||
.andExpect(jsonPath("$.descriptor[0].ext.value", is("value goes here")))
|
||||
.andExpect(jsonPath("$.descriptor[0].rt", is("rt for [name]")))
|
||||
|
||||
.andExpect(jsonPath("$.descriptor[1].id", is("class field [role]")))
|
||||
.andExpect(jsonPath("$.descriptor[1].name", is("role")))
|
||||
.andExpect(jsonPath("$.descriptor[1].type", is("SEMANTIC")))
|
||||
.andExpect(jsonPath("$.descriptor[1].descriptor", hasSize(1)))
|
||||
.andExpect(jsonPath("$.descriptor[1].descriptor[0].id", is("embedded")))
|
||||
.andExpect(jsonPath("$.descriptor[1].ext.id", is("ext [role]")))
|
||||
.andExpect(jsonPath("$.descriptor[1].ext.href", is("https://example.org/samples/ext/role")))
|
||||
.andExpect(jsonPath("$.descriptor[1].ext.value", is("value goes here")))
|
||||
.andExpect(jsonPath("$.descriptor[1].rt", is("rt for [role]")));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@EnableHypermediaSupport(type = HAL)
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
WebMvcEmployeeController employeeController() {
|
||||
return new WebMvcEmployeeController();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,12 +15,17 @@
|
||||
*/
|
||||
package org.springframework.hateoas.support;
|
||||
|
||||
import static org.springframework.hateoas.MediaTypes.*;
|
||||
import static org.springframework.hateoas.mediatype.PropertyUtils.*;
|
||||
import static org.springframework.hateoas.mediatype.alps.Alps.*;
|
||||
import static org.springframework.hateoas.server.reactive.WebFluxLinkBuilder.*;
|
||||
import static reactor.function.TupleUtils.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -30,6 +35,11 @@ import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.IanaLinkRelations;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.mediatype.alps.Alps;
|
||||
import org.springframework.hateoas.mediatype.alps.Descriptor;
|
||||
import org.springframework.hateoas.mediatype.alps.Ext;
|
||||
import org.springframework.hateoas.mediatype.alps.Format;
|
||||
import org.springframework.hateoas.mediatype.alps.Type;
|
||||
import org.springframework.hateoas.server.reactive.WebFluxLinkBuilder;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -174,4 +184,30 @@ public class WebFluxEmployeeController {
|
||||
.build() //
|
||||
);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/profile", produces = ALPS_JSON_VALUE)
|
||||
Alps profile() {
|
||||
|
||||
return Alps.alps() //
|
||||
.doc(doc() //
|
||||
.href("https://example.org/samples/full/doc.html") //
|
||||
.value("value goes here") //
|
||||
.format(Format.TEXT) //
|
||||
.build()) //
|
||||
.descriptor(getExposedProperties(Employee.class).stream() //
|
||||
.map(property -> Descriptor.builder() //
|
||||
.id("class field [" + property.getName() + "]") //
|
||||
.name(property.getName()) //
|
||||
.type(Type.SEMANTIC) //
|
||||
.ext(Ext.builder() //
|
||||
.id("ext [" + property.getName() + "]") //
|
||||
.href("https://example.org/samples/ext/" + property.getName()) //
|
||||
.value("value goes here") //
|
||||
.build()) //
|
||||
.rt("rt for [" + property.getName() + "]") //
|
||||
.descriptor(Collections.singletonList(Descriptor.builder().id("embedded").build())) //
|
||||
.build()) //
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,13 @@
|
||||
*/
|
||||
package org.springframework.hateoas.support;
|
||||
|
||||
import static org.springframework.hateoas.MediaTypes.*;
|
||||
import static org.springframework.hateoas.mediatype.PropertyUtils.*;
|
||||
import static org.springframework.hateoas.mediatype.alps.Alps.*;
|
||||
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -29,6 +33,11 @@ import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.IanaLinkRelations;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.mediatype.alps.Alps;
|
||||
import org.springframework.hateoas.mediatype.alps.Descriptor;
|
||||
import org.springframework.hateoas.mediatype.alps.Ext;
|
||||
import org.springframework.hateoas.mediatype.alps.Format;
|
||||
import org.springframework.hateoas.mediatype.alps.Type;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
@@ -174,4 +183,32 @@ public class WebMvcEmployeeController {
|
||||
.toUri()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
// tag::alps-profile[]
|
||||
@GetMapping(value = "/profile", produces = ALPS_JSON_VALUE)
|
||||
Alps profile() {
|
||||
|
||||
return Alps.alps() //
|
||||
.doc(doc() //
|
||||
.href("https://example.org/samples/full/doc.html") //
|
||||
.value("value goes here") //
|
||||
.format(Format.TEXT) //
|
||||
.build()) //
|
||||
.descriptor(getExposedProperties(Employee.class).stream() //
|
||||
.map(property -> Descriptor.builder() //
|
||||
.id("class field [" + property.getName() + "]") //
|
||||
.name(property.getName()) //
|
||||
.type(Type.SEMANTIC) //
|
||||
.ext(Ext.builder() //
|
||||
.id("ext [" + property.getName() + "]") //
|
||||
.href("https://example.org/samples/ext/" + property.getName()) //
|
||||
.value("value goes here") //
|
||||
.build()) //
|
||||
.rt("rt for [" + property.getName() + "]") //
|
||||
.descriptor(Collections.singletonList(Descriptor.builder().id("embedded").build())) //
|
||||
.build()) //
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
}
|
||||
// end::alps-profile[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user