DATAREST-776, DATAREST-775 - Integration tests for lookup types and nested associations.

Added shop integration test example that shows and tests nested associations and both unique and collection lookup type references.
This commit is contained in:
Oliver Gierke
2016-02-29 19:01:52 +01:00
parent 892409da2c
commit 2be6b2b6a9
16 changed files with 626 additions and 0 deletions

View File

@@ -19,11 +19,13 @@
<module>spring-data-rest-tests-jpa</module>
<module>spring-data-rest-tests-mongodb</module>
<module>spring-data-rest-tests-security</module>
<module>spring-data-rest-tests-shop</module>
<module>spring-data-rest-tests-solr</module>
</modules>
<properties>
<groovy.version>2.4.4</groovy.version>
<source.level>1.8</source.level>
</properties>
<dependencies>

View File

@@ -0,0 +1,31 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-tests</artifactId>
<version>2.5.0.BUILD-SNAPSHOT</version>
</parent>
<name>Spring Data REST Tests - Shop</name>
<artifactId>spring-data-rest-tests-shop</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-data-rest-tests-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-keyvalue</artifactId>
<version>${springdata.keyvalue}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2014-2016 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.tests.shop;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.util.UUID;
import org.springframework.data.annotation.Id;
/**
* @author Oliver Gierke
*/
@Data
@RequiredArgsConstructor
public class Address {
private @Id UUID id = UUID.randomUUID();
private final String street, zipCode, city, state;
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
return String.format("%s, %s %s, %s", street, zipCode, city, state);
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2014-2016 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.tests.shop;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.util.UUID;
import org.springframework.data.annotation.Id;
/**
* @author Oliver Gierke
*/
@Data
@RequiredArgsConstructor
public class Customer {
private final @Id UUID id = UUID.randomUUID();
private final String firstname, lastname;
private final Gender gender;
private final Address address;
static enum Gender {
MALE, FEMALE;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2014-2016 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.tests.shop;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.rest.core.config.Projection;
/**
* @author Oliver Gierke
*/
@Projection(name = "excerpt", types = Customer.class)
public interface CustomerExcerpt {
String getFirstname();
String getLastname();
@Value("#{target.address.toString()}")
String getAddress();
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2014-2016 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.tests.shop;
import java.util.UUID;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
/**
* @author Oliver Gierke
*/
@RepositoryRestResource(excerptProjection = CustomerExcerpt.class)
public interface CustomerRepository extends CrudRepository<Customer, UUID> {
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2014-2016 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.tests.shop;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Reference;
/**
* @author Oliver Gierke
*/
@Data
@EqualsAndHashCode(of = "id")
public class LineItem {
private final @Id UUID id = UUID.randomUUID();
private final String description;
private final BigDecimal price;
private final @Reference Product product;
private final @Reference List<Product> products;
private final @Reference LineItemType type;
private final @Reference List<LineItemType> types;
public LineItem(Product product, LineItemType type) {
this.price = product.getPrice();
this.description = product.getName();
this.type = type;
this.types = Arrays.asList(type, type);
this.product = product;
this.products = Arrays.asList(product, product);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2016 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.tests.shop;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import java.util.UUID;
import org.springframework.data.annotation.Id;
/**
* @author Oliver Gierke
*/
@Getter
@EqualsAndHashCode(of = "id")
@ToString
@RequiredArgsConstructor
public class LineItemType {
private final @Id UUID id = UUID.randomUUID();
private final String name;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2016 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.tests.shop;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.repository.CrudRepository;
/**
* @author Oliver Gierke
*/
public interface LineItemTypeRepository extends CrudRepository<LineItemType, UUID> {
Optional<LineItemType> findByName(String name);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2014-2016 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.tests.shop;
import lombok.Value;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Reference;
/**
* @author Oliver Gierke
*/
@Value
public class Order {
private final @Id UUID id = UUID.randomUUID();
private final List<LineItem> items = new ArrayList<>();
private final @Reference Customer customer;
public Order add(LineItem item) {
this.items.add(item);
return this;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2014-2016 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.tests.shop;
import java.util.UUID;
import org.springframework.data.repository.CrudRepository;
/**
* @author Oliver Gierke
*/
public interface OrderRepository extends CrudRepository<Order, UUID> {
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2016 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.tests.shop;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.math.BigDecimal;
import java.util.UUID;
import org.springframework.data.annotation.Id;
/**
* @author Oliver Gierke
*/
@Value
@RequiredArgsConstructor
public class Product {
private final @Id UUID id = UUID.randomUUID();
private final String name;
private final BigDecimal price;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2016 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.tests.shop;
/**
* @author Oliver Gierke
*/
interface ProductExcerpt {
String getName();
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2016 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.tests.shop;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
/**
* @author Oliver Gierke
*/
@RepositoryRestResource(excerptProjection = Product.class)
public interface ProductRepository extends CrudRepository<Product, Long> {
Optional<Product> findByName(String name);
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2014-2016 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.tests.shop;
import java.math.BigDecimal;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.map.repository.config.EnableMapRepositories;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.tests.shop.Customer.Gender;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceProcessor;
/**
* @author Oliver Gierke
*/
@Configuration
@EnableMapRepositories
public class ShopConfiguration {
@Autowired CustomerRepository customers;
@Autowired OrderRepository orders;
@Autowired ProductRepository products;
@Autowired LineItemTypeRepository lineItemTypes;
@Bean
public ResourceProcessor<Resource<LineItem>> lineItemResourceProcessor() {
return new ResourceProcessor<Resource<LineItem>>() {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceProcessor#process(org.springframework.hateoas.ResourceSupport)
*/
@Override
public Resource<LineItem> process(Resource<LineItem> resource) {
resource.add(new Link("foo", "bar"));
return resource;
}
};
}
@PostConstruct
public void init() {
LineItemType lineItemType = lineItemTypes.save(new LineItemType("good"));
Product guitar = products.save(new Product("Lakewood guitar", new BigDecimal(1299.0)));
Product drums = products.save(new Product("Yamaha Drums", new BigDecimal(2999.0)));
Customer dave = customers.save(new Customer("Dave", "Matthews", Gender.MALE, //
new Address("4711 Some Place", "54321", "Charlottesville", "VA")));
Order order = new Order(dave);
order.add(new LineItem(guitar, lineItemType));
order.add(new LineItem(drums, lineItemType));
orders.save(order);
}
@Configuration
static class SpringDataRestConfiguration extends RepositoryRestConfigurerAdapter {
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter#configureRepositoryRestConfiguration(org.springframework.data.rest.core.config.RepositoryRestConfiguration)
*/
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup().forRepository(ProductRepository.class, Product::getName, ProductRepository::findByName);
config.withEntityLookup().forValueRepository(LineItemTypeRepository.class, LineItemType::getName,
LineItemTypeRepository::findByName);
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2016 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.tests.shop;
import static org.hamcrest.CoreMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.rest.tests.AbstractWebIntegrationTests;
import org.springframework.hateoas.Link;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.ResultActions;
import com.jayway.jsonpath.JsonPath;
/**
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ShopConfiguration.class)
public class ShopIntegrationTests extends AbstractWebIntegrationTests {
@Test
public void rendersRepresentationCorrectly() throws Exception {
Link ordersLink = client.discoverUnique("orders");
String selfLink = JsonPath.parse(client.request(ordersLink).getContentAsString())
.read("$._embedded.orders[0]._links.self.href");
ResultActions actions = client.follow(selfLink)
// Lookup type is rendered as String
.andExpect(jsonPath("items[0].type", is("good")))//
// Invokes ResourceProcessor for nested Resource
.andExpect(jsonPath("items[0]._links.bar").exists());//
// Adds excerpt projection for related product
expectRelatedResource("items[0].product", actions);
expectRelatedResource("items[0].products", actions);
// Adds excerpt projection for root level reference
expectRelatedResource("customer", actions);
}
private static void expectRelatedResource(String name, ResultActions actions) throws Exception {
int dotIndex = name.lastIndexOf('.');
String prefix = dotIndex == -1 ? "" : name.substring(0, dotIndex);
String suffix = dotIndex == -1 ? name : name.substring(dotIndex);
actions.andExpect(jsonPath(prefix.concat(suffix)).doesNotExist());
actions.andExpect(jsonPath(prefix.concat("_links.").concat(suffix)).exists());
actions.andExpect(jsonPath(prefix.concat("_embedded.").concat(suffix)).exists());
}
}