#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:
@@ -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