/*
* Copyright 2014-2017 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;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
import static org.springframework.data.rest.tests.TestMvcClient.*;
import static org.springframework.http.HttpMethod.*;
import java.util.List;
import java.util.Optional;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.support.RepositoryInvoker;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.tests.AbstractControllerIntegrationTests;
import org.springframework.data.rest.webmvc.jpa.Address;
import org.springframework.data.rest.webmvc.jpa.AddressRepository;
import org.springframework.data.rest.webmvc.jpa.CreditCard;
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
import org.springframework.data.rest.webmvc.jpa.Order;
import org.springframework.data.rest.webmvc.jpa.Person;
import org.springframework.data.rest.webmvc.support.DefaultedPageable;
import org.springframework.data.rest.webmvc.support.ETag;
import org.springframework.hateoas.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.HttpRequestMethodNotSupportedException;
/**
* Integration tests for {@link RepositoryEntityController}.
*
* @author Oliver Gierke
* @author Jeremy Rickard
*/
@ContextConfiguration(classes = JpaRepositoryConfig.class)
@Transactional
public class RepositoryEntityControllerIntegrationTests extends AbstractControllerIntegrationTests {
@Autowired RepositoryEntityController controller;
@Autowired AddressRepository repository;
@Autowired RepositoryRestConfiguration configuration;
@Autowired PersistentEntityResourceAssembler assembler;
@Autowired PersistentEntities entities;
@Test(expected = HttpRequestMethodNotSupportedException.class) // DATAREST-217
public void returnsNotFoundForListingEntitiesIfFindAllNotExported() throws Exception {
repository.save(new Address());
RootResourceInformation request = getResourceInformation(Address.class);
controller.getCollectionResource(request, null, null, null);
}
@Test(expected = HttpRequestMethodNotSupportedException.class) // DATAREST-217
public void rejectsEntityCreationIfSaveIsNotExported() throws Exception {
RootResourceInformation request = getResourceInformation(Address.class);
controller.postCollectionResource(request, null, null, MediaType.APPLICATION_JSON_VALUE);
}
@Test // DATAREST-301
public void setsExpandedSelfUriInLocationHeader() throws Exception {
RootResourceInformation information = getResourceInformation(Order.class);
PersistentEntityResource persistentEntityResource = PersistentEntityResource
.build(new Order(new Person()), entities.getRequiredPersistentEntity(Order.class)).build();
ResponseEntity<?> entity = controller.putItemResource(information, persistentEntityResource, 1L, assembler,
ETag.NO_ETAG, MediaType.APPLICATION_JSON_VALUE);
assertThat(entity.getHeaders().getLocation().toString(), not(Matchers.endsWith("{?projection}")));
}
@Test // DATAREST-330
public void exposesHeadForCollectionResourceIfExported() throws Exception {
ResponseEntity<?> entity = controller.headCollectionResource(getResourceInformation(Person.class),
new DefaultedPageable(null, false));
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
}
@Test(expected = ResourceNotFoundException.class) // DATAREST-330
public void doesNotExposeHeadForCollectionResourceIfNotExported() throws Exception {
controller.headCollectionResource(getResourceInformation(CreditCard.class), new DefaultedPageable(null, false));
}
@Test // DATAREST-330
public void exposesHeadForItemResourceIfExported() throws Exception {
Address address = repository.save(new Address());
ResponseEntity<?> entity = controller.headForItemResource(getResourceInformation(Address.class), address.id,
assembler);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
}
@Test(expected = ResourceNotFoundException.class) // DATAREST-330
public void doesNotExposeHeadForItemResourceIfNotExisting() throws Exception {
controller.headForItemResource(getResourceInformation(CreditCard.class), 1L, assembler);
}
@Test // DATAREST-333
public void doesNotExposeMethodsForOptionsIfNotHttpMethodsSupportedForCollectionResource() {
HttpEntity<?> response = controller.optionsForCollectionResource(getResourceInformation(Address.class));
assertAllowHeaders(response, OPTIONS);
}
@Test // DATAREST-333
public void exposesSupportedHttpMethodsInAllowHeaderForOptionsRequestToCollectionResource() {
HttpEntity<?> response = controller.optionsForCollectionResource(getResourceInformation(Person.class));
assertAllowHeaders(response, GET, POST, HEAD, OPTIONS);
}
@Test // DATAREST-333
public void exposesSupportedHttpMethodsInAllowHeaderForOptionsRequestToItemResource() {
HttpEntity<?> response = controller.optionsForItemResource(getResourceInformation(Person.class));
assertAllowHeaders(response, GET, PUT, PATCH, DELETE, HEAD, OPTIONS);
}
@Test // DATAREST-333, DATAREST-348
public void optionsForItermResourceSetsAllowPatchHeader() {
ResponseEntity<?> entity = controller.optionsForItemResource(getResourceInformation(Person.class));
List<String> value = entity.getHeaders().get("Accept-Patch");
assertThat(value).hasSize(3);
assertThat(value,
hasItems(//
RestMediaTypes.JSON_PATCH_JSON.toString(), //
RestMediaTypes.MERGE_PATCH_JSON.toString(), //
MediaType.APPLICATION_JSON_VALUE));
}
@Test // DATAREST-34
public void returnsBodyOnPutForUpdateIfAcceptHeaderPresentByDefault() throws Exception {
RootResourceInformation request = getResourceInformation(Order.class);
Order order = request.getInvoker().invokeSave(new Order(new Person()));
PersistentEntityResource persistentEntityResource = PersistentEntityResource
.build(new Order(new Person()), entities.getRequiredPersistentEntity(Order.class)).build();
assertThat(controller.putItemResource(request, persistentEntityResource, order.getId(), assembler, ETag.NO_ETAG,
MediaType.APPLICATION_JSON_VALUE).hasBody(), is(true));
}
@Test // DATAREST-34
public void returnsBodyForCreatingPutIfAcceptHeaderPresentByDefault() throws HttpRequestMethodNotSupportedException {
RootResourceInformation request = getResourceInformation(Order.class);
PersistentEntityResource persistentEntityResource = PersistentEntityResource
.build(new Order(new Person()), entities.getRequiredPersistentEntity(Order.class)).build();
assertThat(controller.putItemResource(request, persistentEntityResource, 1L, assembler, ETag.NO_ETAG,
MediaType.APPLICATION_JSON_VALUE).hasBody(), is(true));
}
@Test // DATAREST-34
public void returnsBodyForPostIfAcceptHeaderIsPresentByDefault() throws Exception {
RootResourceInformation request = getResourceInformation(Order.class);
PersistentEntityResource persistentEntityResource = PersistentEntityResource
.build(new Order(new Person()), entities.getRequiredPersistentEntity(Order.class)).build();
assertThat(controller
.postCollectionResource(request, persistentEntityResource, assembler, MediaType.APPLICATION_JSON_VALUE)
.hasBody(), is(true));
}
@Test // DATAREST-34
public void doesNotReturnBodyForPostIfNoAcceptHeaderPresentByDefault() throws Exception {
RootResourceInformation request = getResourceInformation(Order.class);
PersistentEntityResource persistentEntityResource = PersistentEntityResource
.build(new Order(new Person()), entities.getRequiredPersistentEntity(Order.class)).build();
assertThat(controller.postCollectionResource(request, persistentEntityResource, assembler, null).hasBody(),
is(false));
assertThat(controller.postCollectionResource(request, persistentEntityResource, assembler, "").hasBody(),
is(false));
}
@Test // DATAREST-581
public void createsEtagForProjectedEntityCorrectly() throws Exception {
Address address = repository.save(new Address());
PersistentEntityResourceAssembler assembler = Mockito.mock(PersistentEntityResourceAssembler.class);
AddressProjection addressProjection = new SpelAwareProxyProjectionFactory()
.createProjection(AddressProjection.class);
PersistentEntityResource resource = PersistentEntityResource
.build(addressProjection, entities.getRequiredPersistentEntity(Address.class)).build();
Mockito.when(assembler.toFullResource(Mockito.any(Object.class))).thenReturn(resource);
ResponseEntity<Resource<?>> entity = controller.getItemResource(getResourceInformation(Address.class), address.id,
assembler, new HttpHeaders());
assertThat(entity.getHeaders().getETag()).isNotNull();
}
@Test // DATAREST-724
public void deletesEntityWithCustomLookupCorrectly() throws Exception {
Address address = repository.save(new Address());
assertThat(repository.findById(address.id)).isNotNull();
RootResourceInformation resourceInformation = getResourceInformation(Address.class);
RepositoryInvoker invoker = spy(resourceInformation.getInvoker());
doReturn(Optional.of(address)).when(invoker).invokeFindById("foo");
RootResourceInformation informationSpy = Mockito.spy(resourceInformation);
doReturn(invoker).when(informationSpy).getInvoker();
controller.deleteItemResource(informationSpy, "foo", ETag.from("0"));
assertThat(repository.findById(address.id)).isEmpty();
}
interface AddressProjection {}
}