DATAREST-219 - Make editing collection-based properties more efficient

This PR is inspired by #86, but had to be written from scratch because it was over 10 months old when this was undertaken. It was unmergeable.

Essentially, by casting existing collections and then iterating over them and making adjustments should render more efficient back end database operations. Only for a PUT should a new collection be created.

This PR not only updates these operations, but provides test cases to back up these edits, i.e. a test case for POSTing to a collection, PUTing to a collection, and DELETE-ing from a collection.

Adds ability to return an empty response with only the Location header populated by looking at the property update's incoming URL.

Original pull request: #128, #86.
This commit is contained in:
Greg Turnquist
2014-01-14 21:50:52 -06:00
committed by Oliver Gierke
parent d59ec3bdd4
commit 1f0b9bd664
5 changed files with 373 additions and 32 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -27,15 +27,23 @@ import org.springframework.http.ResponseEntity;
/**
* @author Oliver Gierke
* @author Greg Turnquist
*/
public class ControllerUtils {
public static final Resource<?> EMPTY_RESOURCE = new Resource<Object>(new Object());
public static final Resources<Resource<?>> EMPTY_RESOURCES = new Resources<Resource<?>>(
Collections.<Resource<?>> emptyList());
public static final Iterable<Resource<?>> EMPTY_RESOURCE_LIST = Collections.emptyList();
public static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class);
/**
* Wrap a resource as a {@link ResourceEntity} and attach given headers and status.
* @param headers
* @param resource
* @param status
* @param <R>
* @return
*/
public static <R extends ResourceSupport> ResponseEntity<ResourceSupport> toResponseEntity(HttpHeaders headers,
R resource, HttpStatus status) {
@@ -46,4 +54,23 @@ public class ControllerUtils {
return new ResponseEntity<ResourceSupport>(resource, hdrs, status);
}
/**
* Return an empty response that is only comprised of a status
* @param status
* @return
*/
public static ResponseEntity<ResourceSupport> toEmptyResponse(HttpStatus status) {
return toResponseEntity(null, EMPTY_RESOURCES, status);
}
/**
* Return an empty response that is only comprised of headers and a status
* @param headers
* @param status
* @return
*/
public static ResponseEntity<ResourceSupport> toEmptyResponse(HttpHeaders headers, HttpStatus status) {
return toResponseEntity(headers, EMPTY_RESOURCES, status);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 the original author or authors.
* Copyright 2012-2014 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.
@@ -18,9 +18,11 @@ package org.springframework.data.rest.webmvc;
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -58,10 +60,12 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerMapping;
/**
* @author Jon Brisbin
* @author Oliver Gierke
* @author Greg Turnquist
*/
@RepositoryRestController
@SuppressWarnings({ "unchecked" })
@@ -188,7 +192,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
}
}
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
}
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.GET, produces = { "application/json",
@@ -291,11 +295,13 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
return ControllerUtils.toResponseEntity(null, new Resource<Object>(EMPTY_RESOURCE_LIST, links), HttpStatus.OK);
}
@RequestMapping(value = BASE_MAPPING, method = { RequestMethod.POST, RequestMethod.PUT }, consumes = {
"application/json", "application/x-spring-data-compact+json", "text/uri-list" })
@RequestMapping(value = BASE_MAPPING, //
method = { RequestMethod.POST, RequestMethod.PUT }, //
consumes = { "application/json", "application/x-spring-data-compact+json", "text/uri-list" })
@ResponseBody
public ResponseEntity<? extends ResourceSupport> createPropertyReference(final RepositoryRestRequest repoRequest,
final @RequestBody Resource<Object> incoming, @PathVariable String id, @PathVariable String property)
final @RequestBody Resources<Object> incoming, @PathVariable String id, @PathVariable String property,
final HttpServletRequest request)
throws NoSuchMethodException {
final RepositoryInvoker invoker = repoRequest.getRepositoryInvoker();
@@ -310,12 +316,16 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
if (prop.property.isCollectionLike()) {
Collection<Object> coll = new ArrayList<Object>();
Collection<Object> coll;
// Either load the exist collection to add to it (POST)
if (HttpMethod.POST.equals(repoRequest.getRequestMethod())) {
coll.addAll((Collection<Object>) prop.propertyValue);
coll = (Collection<Object>) prop.propertyValue;
} else { // Or start from an empty collection to replace it (PUT)
coll = new ArrayList<Object>();
}
// Add to the existing collection
for (Link l : incoming.getLinks()) {
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
coll.add(propVal);
@@ -325,12 +335,16 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
} else if (prop.property.isMap()) {
Map<String, Object> m = new HashMap<String, Object>();
Map<String, Object> m;
// Either load the exist collection to add to it (POST)
if (HttpMethod.POST.equals(repoRequest.getRequestMethod())) {
m.putAll((Map<String, Object>) prop.propertyValue);
m = (Map<String, Object>) prop.propertyValue;
} else { // Or start from an empty collection to replace it (PUT)
m = new HashMap<String, Object>();
}
// Add to the existing collection
for (Link l : incoming.getLinks()) {
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
m.put(l.getRel(), propVal);
@@ -364,7 +378,10 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
doWithReferencedProperty(repoRequest, id, property, handler);
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.CREATED);
final HttpHeaders headers = new HttpHeaders();
headers.set("Location", String.valueOf(request.getRequestURL()));
return ControllerUtils.toEmptyResponse(headers, HttpStatus.CREATED);
}
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.DELETE)
@@ -389,25 +406,27 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
}
if (prop.property.isCollectionLike()) {
Collection<Object> coll = new ArrayList<Object>();
for (Object obj : (Collection<Object>) prop.propertyValue) {
Collection<Object> coll = (Collection<Object>) prop.propertyValue;
Iterator<Object> itr = coll.iterator();
while (itr.hasNext()) {
Object obj = itr.next();
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(obj, null);
String s = propValWrapper.getProperty(prop.entity.getIdProperty()).toString();
if (!propertyId.equals(s)) {
coll.add(obj);
if (propertyId.equals(s)) {
itr.remove();
}
}
prop.wrapper.setProperty(prop.property, coll);
} else if (prop.property.isMap()) {
Map<Object, Object> m = new HashMap<Object, Object>();
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) prop.propertyValue).entrySet()) {
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(entry.getValue(), null);
Map<Object, Object> m = (Map<Object, Object>) prop.propertyValue;
Iterator<Object> itr = m.keySet().iterator();
while (itr.hasNext()) {
Object key = itr.next();
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(m.get(key), null);
String s = propValWrapper.getProperty(prop.entity.getIdProperty()).toString();
if (!propertyId.equals(s)) {
m.put(entry.getKey(), entry.getValue());
if (propertyId.equals(s)) {
itr.remove();
}
}
prop.wrapper.setProperty(prop.property, m);
} else {
prop.wrapper.setProperty(prop.property, null);
}
@@ -422,7 +441,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
doWithReferencedProperty(repoRequest, id, property, handler);
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
}
private Object loadPropertyValue(Class<?> type, String href) {

View File

@@ -1,3 +1,18 @@
/*
* Copyright 2014 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.data.rest.webmvc.convert;
import java.io.BufferedReader;
@@ -10,7 +25,8 @@ import java.util.Collections;
import java.util.List;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
@@ -20,8 +36,9 @@ import org.springframework.http.converter.HttpMessageNotWritableException;
/**
* @author Jon Brisbin
* @author Greg Turnquist
*/
public class UriListHttpMessageConverter implements HttpMessageConverter<Resource<?>> {
public class UriListHttpMessageConverter implements HttpMessageConverter<ResourceSupport> {
private static final List<MediaType> MEDIA_TYPES = new ArrayList<MediaType>();
@@ -34,7 +51,7 @@ public class UriListHttpMessageConverter implements HttpMessageConverter<Resourc
if (null == mediaType) {
return false;
}
return Resource.class.isAssignableFrom(clazz) && mediaType.getSubtype().contains("uri-list");
return ResourceSupport.class.isAssignableFrom(clazz) && mediaType.getSubtype().contains("uri-list");
}
@Override
@@ -48,7 +65,7 @@ public class UriListHttpMessageConverter implements HttpMessageConverter<Resourc
}
@Override
public Resource<?> read(Class<? extends Resource<?>> clazz, HttpInputMessage inputMessage) throws IOException,
public ResourceSupport read(Class<? extends ResourceSupport> clazz, HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
List<Link> links = new ArrayList<Link>();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputMessage.getBody()));
@@ -56,11 +73,11 @@ public class UriListHttpMessageConverter implements HttpMessageConverter<Resourc
while (null != (line = reader.readLine())) {
links.add(new Link(line));
}
return new Resource<Object>(Collections.emptyList(), links);
return new Resources<Object>(Collections.emptyList(), links);
}
@Override
public void write(Resource<?> resource, MediaType contentType, HttpOutputMessage outputMessage) throws IOException,
public void write(ResourceSupport resource, MediaType contentType, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputMessage.getBody()));
for (Link link : resource.getLinks()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -50,6 +50,7 @@ import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilde
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import com.jayway.jsonpath.InvalidPathException;
@@ -57,6 +58,7 @@ import com.jayway.jsonpath.JsonPath;
/**
* @author Oliver Gierke
* @author Greg Turnquist
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@@ -134,6 +136,74 @@ public abstract class AbstractWebIntegrationTests {
return assertHasLinkWithRel(rel, response);
}
protected MockHttpServletResponse postAndGet(Link link, Object payload, MediaType mediaType) throws Exception {
String href;
if (link.isTemplated()) {
href = link.expand().getHref();
} else {
href = link.getHref();
}
MockHttpServletResponse response = mvc.perform(post(href).content(payload.toString()).contentType(mediaType)).//
andExpect(status().isCreated()).//
andExpect(header().string("Location", is(notNullValue()))).//
andReturn().getResponse();
String content = response.getContentAsString();
if (StringUtils.hasText(content)) {
return response;
}
return request(response.getHeader("Location"));
}
protected MockHttpServletResponse putAndGet(Link link, Object payload, MediaType mediaType) throws Exception {
String href;
if (link.isTemplated()) {
href = link.expand().getHref();
} else {
href = link.getHref();
}
MockHttpServletResponse response = mvc.perform(put(href).content(payload.toString()).contentType(mediaType)).//
andExpect(status().isCreated()).//
andExpect(header().string("Location", is(notNullValue()))).//
andReturn().getResponse();
String content = response.getContentAsString();
if (StringUtils.hasText(content)) {
return response;
}
return request(response.getHeader("Location"));
}
protected MockHttpServletResponse deleteAndGet(Link link, MediaType mediaType) throws Exception {
String href;
if (link.isTemplated()) {
href = link.expand().getHref();
} else {
href = link.getHref();
}
MockHttpServletResponse response = mvc.perform(delete(href).contentType(mediaType)).//
andExpect(status().isNoContent()).//
andReturn().getResponse();
String content = response.getContentAsString();
if (StringUtils.hasText(content)) {
return response;
}
return request(response.getHeader("Location"));
}
protected Link assertHasLinkWithRel(String rel, MockHttpServletResponse response) throws Exception {
String content = response.getContentAsString();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -20,11 +20,14 @@ import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Scanner;
import net.minidev.json.JSONArray;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -39,15 +42,23 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.JsonPath;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriTemplate;
/**
* Web integration tests specific to JPA.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@Transactional
@ContextConfiguration(classes = JpaRepositoryConfig.class)
public class JpaWebTests extends AbstractWebIntegrationTests {
private static final MediaType TEXT_URI_LIST = MediaType.valueOf("text/uri-list");
static final String LINK_TO_SIBLINGS_OF = "$._embedded..[?(@.firstName == '%s')]._links.siblings.href[0]";
@Autowired TestDataPopulator loader;
@@ -183,6 +194,203 @@ public class JpaWebTests extends AbstractWebIntegrationTests {
mvc.perform(get(href)).andExpect(status().isOk());
}
/**
* @see DATAREST-219
*/
@Test
public void manipulatePropertyCollectionRestfullyWithMultiplePosts() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String bilbo = mapper.writeValueAsString(new Person("Bilbo", "Baggins"));
String frodo = mapper.writeValueAsString(new Person("Frodo", "Baggins"));
String merry = mapper.writeValueAsString(new Person("Merry", "Baggins"));
String pippin = mapper.writeValueAsString(new Person("Pippin", "Baggins"));
Link peopleLink = discoverUnique("people");
MockHttpServletResponse bilboResponse = postAndGet(peopleLink, bilbo, MediaType.APPLICATION_JSON);
MockHttpServletResponse frodoResponse = postAndGet(peopleLink, frodo, MediaType.APPLICATION_JSON);
MockHttpServletResponse merryResponse = postAndGet(peopleLink, merry, MediaType.APPLICATION_JSON);
MockHttpServletResponse pippinResponse = postAndGet(peopleLink, pippin, MediaType.APPLICATION_JSON);
Link bilboSelfLink = assertHasLinkWithRel(Link.REL_SELF, bilboResponse);
Link merrySelfLink = assertHasLinkWithRel(Link.REL_SELF, merryResponse);
Link pippinSelfLink = assertHasLinkWithRel(Link.REL_SELF, pippinResponse);
Link frodosSiblingsLink = assertHasLinkWithRel("siblings", frodoResponse);
postAndGet(frodosSiblingsLink, bilboSelfLink.getHref(), TEXT_URI_LIST);
postAndGet(frodosSiblingsLink, merrySelfLink.getHref(), TEXT_URI_LIST);
postAndGet(frodosSiblingsLink, pippinSelfLink.getHref(), TEXT_URI_LIST);
MockHttpServletResponse frodosLatestSiblings = request(frodosSiblingsLink);
String[] persons = ((JSONArray) JsonPath.read(frodosLatestSiblings.getContentAsString(), "$._embedded.persons[*].firstName")).toArray(new String[]{});
assertThat(persons.length, equalTo(3));
assertThat(Arrays.asList(persons), hasItems("Bilbo", "Merry", "Pippin"));
}
/**
* @see DATAREST-219
*/
@Test
public void manipulatePropertyCollectionRestfullyWithSinglePost() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String bilbo = mapper.writeValueAsString(new Person("Bilbo", "Baggins"));
String frodo = mapper.writeValueAsString(new Person("Frodo", "Baggins"));
String merry = mapper.writeValueAsString(new Person("Merry", "Baggins"));
String pippin = mapper.writeValueAsString(new Person("Pippin", "Baggins"));
Link peopleLink = discoverUnique("people");
MockHttpServletResponse bilboResponse = postAndGet(peopleLink, bilbo, MediaType.APPLICATION_JSON);
MockHttpServletResponse frodoResponse = postAndGet(peopleLink, frodo, MediaType.APPLICATION_JSON);
MockHttpServletResponse merryResponse = postAndGet(peopleLink, merry, MediaType.APPLICATION_JSON);
MockHttpServletResponse pippinResponse = postAndGet(peopleLink, pippin, MediaType.APPLICATION_JSON);
final Link bilboSelfLink = assertHasLinkWithRel(Link.REL_SELF, bilboResponse);
final Link merrySelfLink = assertHasLinkWithRel(Link.REL_SELF, merryResponse);
final Link pippinSelfLink = assertHasLinkWithRel(Link.REL_SELF, pippinResponse);
Link frodosSiblingsLink = assertHasLinkWithRel("siblings", frodoResponse);
postAndGet(frodosSiblingsLink,
StringUtils.arrayToDelimitedString(new Object[]{bilboSelfLink.getHref(),
merrySelfLink.getHref(), pippinSelfLink.getHref()}, "\n"),
TEXT_URI_LIST);
MockHttpServletResponse frodosLatestSiblings = request(frodosSiblingsLink);
String[] persons = ((JSONArray) JsonPath.read(frodosLatestSiblings.getContentAsString(), "$._embedded.persons[*].firstName")).toArray(new String[]{});
assertThat(persons.length, equalTo(3));
assertThat(Arrays.asList(persons), hasItems("Bilbo", "Merry", "Pippin"));
}
/**
* @see DATAREST-219
*/
@Test
public void manipulatePropertyCollectionRestfullyWithMultiplePuts() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String bilbo = mapper.writeValueAsString(new Person("Bilbo", "Baggins"));
String frodo = mapper.writeValueAsString(new Person("Frodo", "Baggins"));
String merry = mapper.writeValueAsString(new Person("Merry", "Baggins"));
String pippin = mapper.writeValueAsString(new Person("Pippin", "Baggins"));
Link peopleLink = discoverUnique("people");
MockHttpServletResponse bilboResponse = postAndGet(peopleLink, bilbo, MediaType.APPLICATION_JSON);
MockHttpServletResponse frodoResponse = postAndGet(peopleLink, frodo, MediaType.APPLICATION_JSON);
MockHttpServletResponse merryResponse = postAndGet(peopleLink, merry, MediaType.APPLICATION_JSON);
MockHttpServletResponse pippinResponse = postAndGet(peopleLink, pippin, MediaType.APPLICATION_JSON);
Link bilboSelfLink = assertHasLinkWithRel(Link.REL_SELF, bilboResponse);
Link merrySelfLink = assertHasLinkWithRel(Link.REL_SELF, merryResponse);
Link pippinSelfLink = assertHasLinkWithRel(Link.REL_SELF, pippinResponse);
Link frodosSiblingsLink = assertHasLinkWithRel("siblings", frodoResponse);
putAndGet(frodosSiblingsLink, bilboSelfLink.getHref(), TEXT_URI_LIST);
putAndGet(frodosSiblingsLink, merrySelfLink.getHref(), TEXT_URI_LIST);
putAndGet(frodosSiblingsLink, pippinSelfLink.getHref(), TEXT_URI_LIST);
MockHttpServletResponse frodosLatestSiblings = request(frodosSiblingsLink);
String firstName = JsonPath.read(frodosLatestSiblings.getContentAsString(), "$._embedded.person.firstName");
assertThat(firstName, equalTo("Pippin"));
postAndGet(frodosSiblingsLink, merrySelfLink.getHref(), TEXT_URI_LIST);
frodosLatestSiblings = request(frodosSiblingsLink);
String[] persons = ((JSONArray) JsonPath.read(frodosLatestSiblings.getContentAsString(),
"$._embedded.persons[*].firstName")).toArray(new String[]{});
assertThat(persons.length, equalTo(2));
assertThat(Arrays.asList(persons), hasItems("Merry", "Pippin"));
}
/**
* @see DATAREST-219
*/
@Test
public void manipulatePropertyCollectionRestfullyWithSinglePut() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String bilbo = mapper.writeValueAsString(new Person("Bilbo", "Baggins"));
String frodo = mapper.writeValueAsString(new Person("Frodo", "Baggins"));
String merry = mapper.writeValueAsString(new Person("Merry", "Baggins"));
String pippin = mapper.writeValueAsString(new Person("Pippin", "Baggins"));
Link peopleLink = discoverUnique("people");
MockHttpServletResponse bilboResponse = postAndGet(peopleLink, bilbo, MediaType.APPLICATION_JSON);
MockHttpServletResponse frodoResponse = postAndGet(peopleLink, frodo, MediaType.APPLICATION_JSON);
MockHttpServletResponse merryResponse = postAndGet(peopleLink, merry, MediaType.APPLICATION_JSON);
MockHttpServletResponse pippinResponse = postAndGet(peopleLink, pippin, MediaType.APPLICATION_JSON);
Link bilboSelfLink = assertHasLinkWithRel(Link.REL_SELF, bilboResponse);
Link merrySelfLink = assertHasLinkWithRel(Link.REL_SELF, merryResponse);
Link pippinSelfLink = assertHasLinkWithRel(Link.REL_SELF, pippinResponse);
Link frodosSiblingsLink = assertHasLinkWithRel("siblings", frodoResponse);
putAndGet(frodosSiblingsLink,
StringUtils.arrayToDelimitedString(new Object[]{bilboSelfLink.getHref(),
merrySelfLink.getHref()}, "\n"),
TEXT_URI_LIST);
putAndGet(frodosSiblingsLink, pippinSelfLink.getHref(), TEXT_URI_LIST);
MockHttpServletResponse frodosLatestSiblings = request(frodosSiblingsLink);
String firstName = JsonPath.read(frodosLatestSiblings.getContentAsString(), "$._embedded.person.firstName");
assertThat(firstName, equalTo("Pippin"));
postAndGet(frodosSiblingsLink, merrySelfLink.getHref(), TEXT_URI_LIST);
frodosLatestSiblings = request(frodosSiblingsLink);
String[] persons = ((JSONArray) JsonPath.read(frodosLatestSiblings.getContentAsString(),
"$._embedded.persons[*].firstName")).toArray(new String[]{});
assertThat(persons.length, equalTo(2));
assertThat(Arrays.asList(persons), hasItems("Merry", "Pippin"));
}
/**
* @see DATAREST-219
*/
@Test
public void manipulatePropertyCollectionRestfullyWithDelete() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String bilbo = mapper.writeValueAsString(new Person("Bilbo", "Baggins"));
String frodo = mapper.writeValueAsString(new Person("Frodo", "Baggins"));
String merry = mapper.writeValueAsString(new Person("Merry", "Baggins"));
String pippin = mapper.writeValueAsString(new Person("Pippin", "Baggins"));
Link peopleLink = discoverUnique("people");
MockHttpServletResponse bilboResponse = postAndGet(peopleLink, bilbo, MediaType.APPLICATION_JSON);
MockHttpServletResponse frodoResponse = postAndGet(peopleLink, frodo, MediaType.APPLICATION_JSON);
MockHttpServletResponse merryResponse = postAndGet(peopleLink, merry, MediaType.APPLICATION_JSON);
MockHttpServletResponse pippinResponse = postAndGet(peopleLink, pippin, MediaType.APPLICATION_JSON);
Link bilboSelfLink = assertHasLinkWithRel(Link.REL_SELF, bilboResponse);
Link merrySelfLink = assertHasLinkWithRel(Link.REL_SELF, merryResponse);
Link pippinSelfLink = assertHasLinkWithRel(Link.REL_SELF, pippinResponse);
Link frodosSiblingsLink = assertHasLinkWithRel("siblings", frodoResponse);
postAndGet(frodosSiblingsLink, bilboSelfLink.getHref(), TEXT_URI_LIST);
postAndGet(frodosSiblingsLink, merrySelfLink.getHref(), TEXT_URI_LIST);
postAndGet(frodosSiblingsLink, pippinSelfLink.getHref(), TEXT_URI_LIST);
String pippinId = new UriTemplate("/people/{id}").match(pippinSelfLink.getHref()).get("id");
deleteAndGet(new Link(frodosSiblingsLink.getHref() + "/" + pippinId), TEXT_URI_LIST);
MockHttpServletResponse frodosLatestSiblings = request(frodosSiblingsLink);
String[] persons = ((JSONArray) JsonPath.read(frodosLatestSiblings.getContentAsString(), "$._embedded.persons[*].firstName")).toArray(new String[]{});
assertThat(persons.length, equalTo(2));
assertThat(Arrays.asList(persons), hasItems("Bilbo", "Merry"));
}
private String readFile(String name) throws Exception {
ClassPathResource file = new ClassPathResource(name, getClass());