DATAREST-93 - More cleanups.

Merged core and repository modules into core. Renamed some packages for consistency in naming and in preparation to break up some package cycles. Removed @BaseUri and the according resolver. Refactored controllers a bit to have more reusable chunks of code.
This commit is contained in:
Oliver Gierke
2013-07-18 16:39:11 +02:00
parent 0f325bb0a0
commit d2c2ec8262
165 changed files with 1736 additions and 1661 deletions

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2012-2013 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.convert;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.UUID;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.DefaultFormattingConversionService;
/**
* Tests to ensure the {@link DelegatingConversionService} properly delegates conversions to the
* {@link org.springframework.core.convert.ConversionService} that is appropriate for the given source and return types.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class DelegatingConversionServiceUnitTests {
private static final UUID RANDOM_UUID = UUID.fromString("9deccfd7-f892-4e26-a4d5-c92893392e78");
@Mock ConversionService conversionService;
DelegatingConversionService delegatingConversionService;
@Before
public void setup() {
DefaultFormattingConversionService cs = new DefaultFormattingConversionService(false);
cs.addConverter(UUIDConverter.INSTANCE);
delegatingConversionService = new DelegatingConversionService(conversionService, cs);
when(conversionService.canConvert(String.class, UUID.class)).thenReturn(false);
when(conversionService.canConvert(UUID.class, String.class)).thenReturn(false);
}
@Test
public void shouldDelegateToProperConversionService() {
assertThat(delegatingConversionService.canConvert(String.class, UUID.class), is(true));
assertThat(delegatingConversionService.convert(RANDOM_UUID.toString(), UUID.class), is(RANDOM_UUID));
verifyConversionService();
}
@Test
public void shouldConvertUUIDToString() {
assertThat(delegatingConversionService.canConvert(UUID.class, String.class), is(true));
assertThat(delegatingConversionService.convert(RANDOM_UUID, String.class), is(RANDOM_UUID.toString()));
verifyConversionService();
}
private void verifyConversionService() {
verify(conversionService, times(0)).convert(Matchers.any(String.class), eq(UUID.class));
verify(conversionService, times(0)).convert(Matchers.any(UUID.class), eq(String.class));
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2013 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.core;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.core.domain.jpa.Person;
import org.springframework.data.rest.core.domain.jpa.PersonRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Base class for integration tests loading {@link RepositoryTestsConfig} and populating the {@link PersonRepository}
* with a {@link Person}.
*
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RepositoryTestsConfig.class)
@Transactional
public abstract class AbstractIntegrationTests {
@Autowired PersonRepository repository;
@Before
public void populateDatabase() {
repository.save(new Person("John", "Doe"));
}
}

View File

@@ -62,4 +62,9 @@ public class PathUnitTests {
public void doesNotMatchIfDifferent() {
assertThat(new Path("/foobar").matches("barfoo"), is(false));
}
@Test
public void doesNotPrefixAbsoluteUris() {
assertThat(new Path("http://localhost").toString(), is("http://localhost"));
}
}

View File

@@ -0,0 +1,32 @@
package org.springframework.data.rest.core;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.config.ResourceMapping;
import org.springframework.data.rest.core.domain.jpa.ConfiguredPersonRepository;
/**
* Tests to check that {@link ResourceMapping}s are handled correctly.
*
* @author Jon Brisbin
*/
@SuppressWarnings("deprecation")
public class RepositoryRestConfigurationIntegrationTests extends AbstractIntegrationTests {
@Autowired RepositoryRestConfiguration config;
@Test
public void shouldProvideResourceMappingForConfiguredRepository() throws Exception {
ResourceMapping mapping = config.getResourceMappingForRepository(ConfiguredPersonRepository.class);
assertThat(mapping, notNullValue());
assertThat(mapping.getRel(), is("people"));
assertThat(mapping.getPath(), is("people"));
assertThat(mapping.isExported(), is(false));
}
}

View File

@@ -0,0 +1,62 @@
package org.springframework.data.rest.core;
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.context.annotation.Import;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.UriDomainClassConverter;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.domain.jpa.ConfiguredPersonRepository;
import org.springframework.data.rest.core.domain.jpa.JpaRepositoryConfig;
import org.springframework.data.rest.core.domain.jpa.Person;
import org.springframework.data.rest.core.domain.jpa.PersonRepository;
import org.springframework.format.support.DefaultFormattingConversionService;
/**
* @author Jon Brisbin
*/
@Configuration
@Import({ JpaRepositoryConfig.class })
public class RepositoryTestsConfig {
@Autowired private ApplicationContext appCtx;
@Bean
public Repositories repositories() {
return new Repositories(appCtx);
}
@SuppressWarnings("deprecation")
@Bean
public RepositoryRestConfiguration config() {
RepositoryRestConfiguration config = new RepositoryRestConfiguration();
config.setResourceMappingForDomainType(Person.class).setRel("person");
config.setResourceMappingForRepository(ConfiguredPersonRepository.class).setRel("people").setPath("people")
.setExported(false);
config.setResourceMappingForRepository(PersonRepository.class).setRel("people").setPath("people")
.addResourceMappingFor("findByFirstName").setRel("firstname").setPath("firstname");
return config;
}
@Bean
public DefaultFormattingConversionService defaultConversionService() {
return new DefaultFormattingConversionService();
}
@Bean
public DomainClassConverter<?> domainClassConverter() {
return new DomainClassConverter<DefaultFormattingConversionService>(defaultConversionService());
}
@Bean
public UriDomainClassConverter uriDomainClassConverter() {
return new UriDomainClassConverter(repositories(), domainClassConverter());
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2012-2013 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.core.config;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.springframework.data.rest.core.support.ResourceMappingUtils.*;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.data.rest.core.domain.jpa.Person;
import org.springframework.data.rest.core.mapping.ResourceMapping;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.core.EvoInflectorRelProvider;
/**
* Ensure the {@link ResourceMapping} components convey the correct information.
*
* @author Jon Brisbin
*/
@SuppressWarnings("deprecation")
public class ResourceMappingUnitTests {
RelProvider relProvider = new EvoInflectorRelProvider();
@Test
public void shouldDetectPathAndRemoveLeadingSlashIfAny() {
org.springframework.data.rest.core.config.ResourceMapping mapping = new org.springframework.data.rest.core.config.ResourceMapping(
findRel(AnnotatedWithLeadingSlashPersonRepository.class),
findPath(AnnotatedWithLeadingSlashPersonRepository.class),
findExported(AnnotatedWithLeadingSlashPersonRepository.class));
// The rel attribute defaults to class name
assertThat(mapping.getRel(), is("annotatedWithLeadingSlashPerson"));
assertThat(mapping.getPath(), is("people"));
// The exported defaults to true
assertThat(mapping.isExported(), is(true));
}
@Test
public void shouldDetectPathAndRemoveLeadingSlashIfAnyOnMethod() throws Exception {
Method method = AnnotatedWithLeadingSlashPersonRepository.class.getMethod("findByFirstName", String.class,
Pageable.class);
org.springframework.data.rest.core.config.ResourceMapping mapping = new org.springframework.data.rest.core.config.ResourceMapping(
findRel(method), findPath(method), findExported(method));
// The rel attribute defaults to class name
assertThat(mapping.getRel(), is("findByFirstName"));
assertThat(mapping.getPath(), is("firstname"));
// The exported defaults to true
assertThat(mapping.isExported(), is(true));
}
@Test
public void shouldReturnDefaultIfPathContainsOnlySlashTextOnMethod() throws Exception {
Method method = AnnotatedWithLeadingSlashPersonRepository.class.getMethod("findByLastName", String.class,
Pageable.class);
org.springframework.data.rest.core.config.ResourceMapping mapping = new org.springframework.data.rest.core.config.ResourceMapping(
findRel(method), findPath(method), findExported(method));
// The rel defaults to method name
assertThat(mapping.getRel(), is("findByLastName"));
// The path contains only a leading slash therefore defaults to method name
assertThat(mapping.getPath(), is("findByLastName"));
// The exported defaults to true
assertThat(mapping.isExported(), is(true));
}
@RestResource(path = "/people")
interface AnnotatedWithLeadingSlashPersonRepository {
@RestResource(path = "/firstname")
Page<Person> findByFirstName(@Param("firstName") String firstName, Pageable pageable);
@RestResource(path = " / ")
Page<Person> findByLastName(@Param("lastName") String firstName, Pageable pageable);
}
}

View File

@@ -0,0 +1,120 @@
package org.springframework.data.rest.core.context;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.context.annotation.Import;
import org.springframework.data.rest.core.RepositoryTestsConfig;
import org.springframework.data.rest.core.domain.jpa.AnnotatedPersonEventHandler;
import org.springframework.data.rest.core.domain.jpa.Person;
import org.springframework.data.rest.core.domain.jpa.PersonBeforeSaveHandler;
import org.springframework.data.rest.core.domain.jpa.PersonRepository;
import org.springframework.data.rest.core.event.AfterCreateEvent;
import org.springframework.data.rest.core.event.AfterDeleteEvent;
import org.springframework.data.rest.core.event.AfterLinkDeleteEvent;
import org.springframework.data.rest.core.event.AfterLinkSaveEvent;
import org.springframework.data.rest.core.event.AfterSaveEvent;
import org.springframework.data.rest.core.event.AnnotatedHandlerBeanPostProcessor;
import org.springframework.data.rest.core.event.BeforeCreateEvent;
import org.springframework.data.rest.core.event.BeforeDeleteEvent;
import org.springframework.data.rest.core.event.BeforeLinkDeleteEvent;
import org.springframework.data.rest.core.event.BeforeLinkSaveEvent;
import org.springframework.data.rest.core.event.BeforeSaveEvent;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Tests around the {@link org.springframework.context.ApplicationEvent} handling abstractions.
*
* @author Jon Brisbin
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
public class RepositoryEventIntegrationTests {
@Configuration
@Import({ RepositoryTestsConfig.class })
static class RepositoryEventTestsConfig {
@Bean
public PersonBeforeSaveHandler personBeforeSaveHandler() {
return new PersonBeforeSaveHandler();
}
@Bean
public AnnotatedPersonEventHandler beforeSaveHandler() {
return new AnnotatedPersonEventHandler();
}
@Bean
public AnnotatedHandlerBeanPostProcessor annotatedHandlerBeanPostProcessor() {
return new AnnotatedHandlerBeanPostProcessor();
}
}
@Autowired ApplicationContext appCtx;
@Autowired PersonRepository people;
Person person;
@Before
public void setup() {
person = people.save(new Person("Jane", "Doe"));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchBeforeCreate() throws Exception {
appCtx.publishEvent(new BeforeCreateEvent(person));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchAfterCreate() throws Exception {
appCtx.publishEvent(new AfterCreateEvent(person));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchBeforeSave() throws Exception {
appCtx.publishEvent(new BeforeSaveEvent(person));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchAfterSave() throws Exception {
appCtx.publishEvent(new AfterSaveEvent(person));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchBeforeDelete() throws Exception {
appCtx.publishEvent(new BeforeDeleteEvent(person));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchAfterDelete() throws Exception {
appCtx.publishEvent(new AfterDeleteEvent(person));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchBeforeLinkSave() throws Exception {
appCtx.publishEvent(new BeforeLinkSaveEvent(person, new Object()));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchAfterLinkSave() throws Exception {
appCtx.publishEvent(new AfterLinkSaveEvent(person, new Object()));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchBeforeLinkDelete() throws Exception {
appCtx.publishEvent(new BeforeLinkDeleteEvent(person, new Object()));
}
@Test(expected = RuntimeException.class)
public void shouldDispatchAfterLinkDelete() throws Exception {
appCtx.publishEvent(new AfterLinkDeleteEvent(person, new Object()));
}
}

View File

@@ -0,0 +1,46 @@
package org.springframework.data.rest.core.context;
import org.junit.Test;
import org.junit.runner.RunWith;
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.context.annotation.Import;
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
import org.springframework.data.rest.core.RepositoryTestsConfig;
import org.springframework.data.rest.core.domain.jpa.Person;
import org.springframework.data.rest.core.event.BeforeSaveEvent;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Tests to check the {@link org.springframework.validation.Validator} integration.
*
* @author Jon Brisbin
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
public class ValidatorIntegrationTests {
@Configuration
@Import({ RepositoryTestsConfig.class })
static class ValidatorTestsConfig {
@Bean
public ValidatingRepositoryEventListener validatingListener() {
return new ValidatingRepositoryEventListener();
}
}
@Autowired ApplicationContext appCtx;
@Test(expected = RepositoryConstraintViolationException.class)
public void shouldValidateLastName() throws Exception {
appCtx.publishEvent(new BeforeSaveEvent(new Person()));
}
}

View File

@@ -0,0 +1,45 @@
package org.springframework.data.rest.core.domain.jpa;
import org.springframework.data.rest.core.annotation.HandleAfterCreate;
import org.springframework.data.rest.core.annotation.HandleAfterDelete;
import org.springframework.data.rest.core.annotation.HandleAfterLinkDelete;
import org.springframework.data.rest.core.annotation.HandleAfterLinkSave;
import org.springframework.data.rest.core.annotation.HandleAfterSave;
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
import org.springframework.data.rest.core.annotation.HandleBeforeDelete;
import org.springframework.data.rest.core.annotation.HandleBeforeLinkDelete;
import org.springframework.data.rest.core.annotation.HandleBeforeLinkSave;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
/**
* @author Jon Brisbin
*/
@RepositoryEventHandler(Person.class)
public class AnnotatedPersonEventHandler {
@HandleAfterCreate
@HandleAfterDelete
@HandleAfterSave
public void handleAfter(Person p) {
throw new RuntimeException();
}
@HandleAfterLinkDelete
@HandleAfterLinkSave
public void handleAfterLink(Person p, Object o) {
throw new RuntimeException();
}
@HandleBeforeCreate
@HandleBeforeDelete
@HandleBeforeSave
public void handleBefore(Person p) {
throw new RuntimeException();
}
@HandleBeforeLinkDelete
@HandleBeforeLinkSave
public void handleBeforeLink(Person p, Object o) {
throw new RuntimeException();
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.data.rest.core.domain.jpa;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.rest.core.annotation.RestResource;
/**
* A repository to manage {@link org.springframework.data.rest.core.domain.jpa.Person}s.
*
* @author Jon Brisbin
*/
@RestResource(rel = "people", exported = false)
@NoRepositoryBean
public interface AnnotatedPersonRepository extends CrudRepository<Person, Long> {}

View File

@@ -0,0 +1,12 @@
package org.springframework.data.rest.core.domain.jpa;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
/**
* A repository to manage {@link Person}s.
*
* @author Jon Brisbin
*/
@NoRepositoryBean
public interface ConfiguredPersonRepository extends CrudRepository<Person, Long> {}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2013 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.core.domain.jpa;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @author Oliver Gierke
*/
@Entity
public class CreditCard {
@Id Long id;
String creditCardNumber;
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2013 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.core.domain.jpa;
import org.springframework.data.repository.CrudRepository;
/**
* @author Oliver Gierke
*/
interface CreditCardRepository extends CrudRepository<CreditCard, Long> {
CreditCard findByCreditCardNumber(String creditCardNumber);
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2012-2013 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.core.domain.jpa;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaDialect;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @author Jon Brisbin
* @author Oliver Gierke
*/
@Configuration
@ComponentScan
@EnableJpaRepositories
@EnableTransactionManagement
public class JpaRepositoryConfig {
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org.springframework.data.rest.core.ValidationErrors");
return ms;
}
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public JpaDialect jpaDialect() {
return new HibernateJpaDialect();
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2013 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.core.domain.jpa;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* @author Oliver Gierke
*/
@Entity
@Table(name = "ORDERS")
public class Order {
private @Id Long id;
private @ManyToOne Person creator;
public Order(Person creator) {
this.creator = creator;
}
protected Order() {
}
public Long getId() {
return id;
}
public Person getCreator() {
return creator;
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2013 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.core.domain.jpa;
import org.springframework.data.repository.CrudRepository;
/**
* @author Oliver Gierke
*/
public interface OrderRepository extends CrudRepository<Order, Long> {
}

View File

@@ -0,0 +1,80 @@
package org.springframework.data.rest.core.domain.jpa;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.PrePersist;
/**
* An entity that represents a person.
*
* @author Jon Brisbin
*/
@Entity
public class Person {
@Id @GeneratedValue private Long id;
private String firstName;
private String lastName;
@OneToMany private List<Person> siblings = Collections.emptyList();
private Date created;
public Person() {}
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public Long getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Person addSibling(Person p) {
if (siblings == Collections.EMPTY_LIST) {
siblings = new ArrayList<Person>();
}
siblings.add(p);
return this;
}
public List<Person> getSiblings() {
return siblings;
}
public void setSiblings(List<Person> siblings) {
this.siblings = siblings;
}
public Date getCreated() {
return created;
}
@PrePersist
private void prePersist() {
this.created = Calendar.getInstance().getTime();
}
}

View File

@@ -0,0 +1,13 @@
package org.springframework.data.rest.core.domain.jpa;
import org.springframework.data.rest.core.event.AbstractRepositoryEventListener;
/**
* @author Jon Brisbin
*/
public class PersonBeforeSaveHandler extends AbstractRepositoryEventListener<Person> {
@Override
protected void onBeforeSave(Person person) {
throw new RuntimeException();
}
}

View File

@@ -0,0 +1,33 @@
package org.springframework.data.rest.core.domain.jpa;
import static org.springframework.util.ClassUtils.*;
import static org.springframework.util.StringUtils.*;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* A test {@link Validator} that checks for non-blank names.
*
* @author Jon Brisbin
*/
@Component
@HandleBeforeSave
public class PersonNameValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return isAssignable(clazz, Person.class);
}
@Override
public void validate(Object target, Errors errors) {
Person p = (Person) target;
if (!hasText(p.getLastName())) {
errors.rejectValue("lastName", "blank", "Last name cannot be blank");
}
}
}

View File

@@ -0,0 +1,30 @@
package org.springframework.data.rest.core.domain.jpa;
import java.util.Date;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
/**
* A repository to manage {@link Person}s.
*
* @author Jon Brisbin
*/
@RestResource(rel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
@RestResource(rel = "firstname", path = "firstname")
Page<Person> findByFirstName(@Param("firstName") String firstName, Pageable pageable);
Page<Person> findByCreatedGreaterThan(@Param("date") Date date, Pageable pageable);
@Query("select p from Person p where p.created > :date")
Page<Person> findByCreatedUsingISO8601Date(@Param("date") @DateTimeFormat(iso = ISO.DATE_TIME) Date date,
Pageable pageable);
}

View File

@@ -0,0 +1,12 @@
package org.springframework.data.rest.core.domain.jpa;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
/**
* A repository to manage {@link Person}s.
*
* @author Jon Brisbin
*/
@NoRepositoryBean
public interface PlainPersonRepository extends CrudRepository<Person, Long> {}

View File

@@ -0,0 +1,32 @@
package org.springframework.data.rest.core.domain.mongodb;
import java.net.UnknownHostException;
import com.mongodb.Mongo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
/**
* @author Jon Brisbin
*/
@Configuration
@ComponentScan(basePackageClasses = { MongoDbRepositoryConfig.class })
@EnableMongoRepositories
public class MongoDbRepositoryConfig {
@Bean
public MongoDbFactory mongoDbFactory() throws UnknownHostException {
return new SimpleMongoDbFactory(new Mongo("localhost"), "spring-data-rest");
}
@Bean
public MongoTemplate mongoTemplate() throws UnknownHostException {
return new MongoTemplate(mongoDbFactory());
}
}

View File

@@ -0,0 +1,46 @@
package org.springframework.data.rest.core.domain.mongodb;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* @author Jon Brisbin
*/
@Document
public class Profile {
@Id private String id;
private String name;
private String type;
public Profile() {}
public Profile(String id, String name, String type) {
this.id = id;
this.name = name;
this.type = type;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public Profile setName(String name) {
this.name = name;
return this;
}
public String getType() {
return type;
}
public Profile setType(String type) {
this.type = type;
return this;
}
}

View File

@@ -0,0 +1,20 @@
package org.springframework.data.rest.core.domain.mongodb;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author Jon Brisbin
*/
@Component
public class ProfileLoader implements InitializingBean {
@Autowired private ProfileRepository profiles;
@Override
public void afterPropertiesSet() throws Exception {
profiles.save(new Profile("jdoe", "jdoe", "account"));
}
}

View File

@@ -0,0 +1,11 @@
package org.springframework.data.rest.core.domain.mongodb;
import org.bson.types.ObjectId;
import org.springframework.data.repository.CrudRepository;
/**
* Repository for managing {@link Profile}s in MongoDB.
*
* @author Jon Brisbin
*/
public interface ProfileRepository extends CrudRepository<Profile, ObjectId> {}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2013 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.core.invoke;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.AbstractIntegrationTests;
import org.springframework.data.rest.core.domain.jpa.Order;
import org.springframework.data.rest.core.domain.jpa.OrderRepository;
import org.springframework.data.rest.core.domain.jpa.Person;
import org.springframework.data.rest.core.domain.jpa.PersonRepository;
/**
* Integration tests for {@link ReflectionRepositoryInvoker}.
*
* @author Oliver Gierke
*/
public class ReflectionRepositoryInvokerIntegrationTests extends AbstractIntegrationTests {
@Autowired Repositories repositories;
@Autowired ConversionService conversionService;
@Autowired PersonRepository repository;
@Autowired OrderRepository orderRepository;
RepositoryInformation information;
RepositoryInvoker invoker;
@Before
public void setUp() {
information = repositories.getRepositoryInformationFor(Person.class);
invoker = new ReflectionRepositoryInvoker(repository, information, conversionService);
}
@Test
public void invokesFindOneWithStringIdCorrectly() {
Person person = repository.findAll().iterator().next();
assertThat(person, is(notNullValue()));
Object result = invoker.invokeFindOne(person.getId().toString());
assertThat(result, is(instanceOf(Person.class)));
}
@Test
public void invokesFindAllWithoutPageableCorrectly() {
Iterable<Object> result = invoker.invokeFindAll((Pageable) null);
assertThat(result, is(instanceOf(Page.class)));
}
@Test
public void invokesFindAllWithPageableCorrectly() {
Iterable<Object> result = invoker.invokeFindAll(new PageRequest(0, 10));
assertThat(result, is(instanceOf(Page.class)));
}
@Test
public void fallsBackToPlainFindAllIfRepositoryIsNotPaging() {
ReflectionRepositoryInvoker invoker = new ReflectionRepositoryInvoker(orderRepository,
repositories.getRepositoryInformationFor(Order.class), conversionService);
Iterable<Object> result = invoker.invokeFindAll(new PageRequest(0, 10));
assertThat(result, is(instanceOf(List.class)));
}
@Test
public void invokesQueryMethod() throws Exception {
HashMap<String, String[]> parameters = new HashMap<String, String[]>();
parameters.put("firstName", new String[] { "John" });
Method method = PersonRepository.class.getMethod("findByFirstName", String.class, Pageable.class);
Object result = invoker.invokeQueryMethod(method, parameters, null, null);
assertThat(result, is(instanceOf(Page.class)));
}
@Test
public void considersFormattingAnnotationsOnQueryMethodParameters() throws Exception {
HashMap<String, String[]> parameters = new HashMap<String, String[]>();
parameters.put("date", new String[] { "2013-07-18T10:49:00.000+02:00" });
Method method = PersonRepository.class.getMethod("findByCreatedUsingISO8601Date", Date.class, Pageable.class);
Object result = invoker.invokeQueryMethod(method, parameters, null, null);
assertThat(result, is(instanceOf(Page.class)));
Page<?> page = (Page<?>) result;
assertThat(page.getNumberOfElements(), is(1));
}
}

View File

@@ -0,0 +1,72 @@
package org.springframework.data.rest.core.invoke;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.springframework.util.ReflectionUtils.*;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.core.domain.jpa.PersonRepository;
import org.springframework.data.rest.core.invoke.RepositoryMethod;
import org.springframework.data.rest.core.support.Methods;
import org.springframework.util.ReflectionUtils;
/**
* Tests to verify the integrity of the {@link RepositoryMethod} abstraction.
*
* @author Jon Brisbin
*/
public class RepositoryMethodUnitTests {
Map<String, RepositoryMethod> methods = new HashMap<String, RepositoryMethod>();
RepositoryMethod method;
@Before
public void setup() {
doWithMethods(PersonRepository.class, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
String name = method.getName();
RepositoryMethod repoMethod = new RepositoryMethod(method);
methods.put(name, repoMethod);
}
}, Methods.USER_METHODS);
method = methods.get("findByFirstName");
}
@Test
public void shouldFindSimpleQueryMethods() throws Exception {
assertThat(method, notNullValue());
}
@Test
public void shouldFindPageableInformationOnMethod() throws Exception {
assertThat(method, notNullValue());
assertThat(method.isPageable(), is(true));
}
@Test
public void shouldNotFindSortInformationOnMethod() throws Exception {
assertThat(method, notNullValue());
assertThat(method.isSortable(), is(false));
}
@Test
public void shouldProvideParameterClassTypes() throws Exception {
assertThat(method, notNullValue());
assertThat(method.getParameters().get(0).getParameterType(), is(typeCompatibleWith(String.class)));
assertThat(method.getParameters().get(1).getParameterType(), is(typeCompatibleWith(Pageable.class)));
}
@Test
public void shouldProvideParameterNames() throws Exception {
assertThat(method, notNullValue());
assertThat(method.getParameterNames(), contains("firstName", "arg1"));
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2013 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.core.mapping;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.repository.Repository;
import org.springframework.data.rest.core.Path;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.data.rest.core.mapping.CollectionResourceMapping;
import org.springframework.data.rest.core.mapping.RepositoryCollectionResourceMapping;
import org.springframework.data.rest.core.mapping.ResourceMapping;
/**
* Unit tests for {@link RepositoryCollectionResourceMapping}.
*
* @author Oliver Gierke
*/
public class RepositoryCollectionResourceMappingUnitTests {
@Test
public void buildsDefaultMappingForRepository() {
CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(PersonRepository.class);
assertThat(mapping.getPath(), is(new Path("persons")));
assertThat(mapping.getRel(), is("persons"));
assertThat(mapping.getSingleResourceRel(), is("person"));
assertThat(mapping.isExported(), is(true));
}
@Test
public void honorsAnnotatedsMapping() {
CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(AnnotatedPersonRepository.class);
assertThat(mapping.getPath(), is(new Path("bar")));
assertThat(mapping.getRel(), is("foo"));
assertThat(mapping.getSingleResourceRel(), is("annotatedPerson"));
assertThat(mapping.isExported(), is(false));
}
@Test
public void repositoryAnnotationTrumpsDomainTypeMapping() {
CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(
AnnotatedAnnotatedPersonRepository.class);
assertThat(mapping.getPath(), is(new Path("/trumpsAll")));
assertThat(mapping.getRel(), is("foo"));
assertThat(mapping.getSingleResourceRel(), is("annotatedPerson"));
assertThat(mapping.isExported(), is(true));
}
@Test
public void doesNotExposeRepositoryForPublicDomainTypeIfRepoIsPackageProtected() {
ResourceMapping mapping = new RepositoryCollectionResourceMapping(PackageProtectedRepository.class);
assertThat(mapping.isExported(), is(false));
}
public static class Person {}
@RestResource(path = "bar", rel = "foo", exported = false)
static class AnnotatedPerson {}
public interface PersonRepository extends Repository<Person, Long> {}
interface AnnotatedPersonRepository extends Repository<AnnotatedPerson, Long> {}
@RestResource(path = "trumpsAll")
interface AnnotatedAnnotatedPersonRepository extends Repository<AnnotatedPerson, Long> {}
public static class PublicClass {}
static interface PackageProtectedRepository extends Repository<PublicClass, Long> {}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2013 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.core.mapping;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.data.repository.Repository;
import org.springframework.data.rest.core.Path;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.data.rest.core.mapping.RepositoryCollectionResourceMapping;
import org.springframework.data.rest.core.mapping.RepositoryMethodResourceMapping;
import org.springframework.data.rest.core.mapping.ResourceMapping;
/**
* @author Oliver Gierke
*/
public class RepositoryMethodResourceMappingUnitTests {
RepositoryCollectionResourceMapping resourceMapping = new RepositoryCollectionResourceMapping(PersonRepository.class);
@Test
public void foo() throws Exception {
Method method = PersonRepository.class.getMethod("findByLastname", String.class);
ResourceMapping mapping = new RepositoryMethodResourceMapping(method, resourceMapping);
assertThat(mapping.getPath(), is(new Path("findByLastname")));
}
@Test
public void usesConfiguredNameWithLeadingSlash() throws Exception {
Method method = PersonRepository.class.getMethod("findByFirstname", String.class);
ResourceMapping mapping = new RepositoryMethodResourceMapping(method, resourceMapping);
assertThat(mapping.getPath(), is(new Path("bar")));
}
static class Person {}
interface PersonRepository extends Repository<Person, Long> {
Iterable<Person> findByLastname(String lastname);
@RestResource(path = "/bar")
Iterable<Person> findByFirstname(String firstname);
@RestResource(path = "foo")
Iterable<Person> findByEmailAddress(String email);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2013 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.core.mapping;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.domain.jpa.CreditCard;
import org.springframework.data.rest.core.domain.jpa.JpaRepositoryConfig;
import org.springframework.data.rest.core.domain.jpa.Person;
import org.springframework.data.rest.core.mapping.ResourceMappings;
import org.springframework.data.rest.core.mapping.ResourceMetadata;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests for {@link ResourceMappings}.
*
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JpaRepositoryConfig.class)
@Transactional
public class ResourceMappingsIntegrationTest {
@Autowired ListableBeanFactory factory;
ResourceMappings mappings;
@Before
public void setUp() {
Repositories repositories = new Repositories(factory);
this.mappings = new ResourceMappings(new RepositoryRestConfiguration(), repositories);
}
@Test
public void detectsAllMappings() {
assertThat(mappings, is(Matchers.<ResourceMetadata> iterableWithSize(6)));
}
@Test
public void exportsResourceAndSearchesForPersons() {
ResourceMetadata personMappings = mappings.getMappingFor(Person.class);
assertThat(personMappings.isExported(), is(true));
assertThat(personMappings.getSearchResourceMappings().isExported(), is(true));
}
@Test
public void doesNotExportAnyMappingsForHiddenRepository() {
ResourceMetadata creditCardMapping = mappings.getMappingFor(CreditCard.class);
assertThat(creditCardMapping.isExported(), is(false));
assertThat(creditCardMapping.getSearchResourceMappings().isExported(), is(false));
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2013 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.core.mapping;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.rest.core.Path;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.data.rest.core.mapping.CollectionResourceMapping;
import org.springframework.data.rest.core.mapping.TypeBasedCollectionResourceMapping;
/**
* Unit tests for {@link TypeBasedCollectionResourceMapping}.
*
* @author Oliver Gierke
*/
public class TypeBasedCollectionResourceMappingUnitTest {
@Test
public void defaultsMappingsByType() {
CollectionResourceMapping mapping = new TypeBasedCollectionResourceMapping(Sample.class);
assertThat(mapping.getPath(), is(new Path("sample")));
assertThat(mapping.getRel(), is("samples"));
assertThat(mapping.getSingleResourceRel(), is("sample"));
assertThat(mapping.isExported(), is(true));
}
@Test
public void usesCustomizedRel() {
CollectionResourceMapping mapping = new TypeBasedCollectionResourceMapping(CustomizedSample.class);
assertThat(mapping.getPath(), is(new Path("customizedSample")));
assertThat(mapping.getRel(), is("myRel"));
assertThat(mapping.getSingleResourceRel(), is("customizedSample"));
assertThat(mapping.isExported(), is(true));
}
/**
* @see DATAREST-99
*/
@Test
public void doesNotExportNonPublicTypesByDefault() {
CollectionResourceMapping mapping = new TypeBasedCollectionResourceMapping(HiddenSample.class);
assertThat(mapping.isExported(), is(false));
}
public interface Sample {}
interface HiddenSample {}
@RestResource(rel = "myRel")
interface CustomizedSample {
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2013 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.core.support;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.springframework.data.rest.core.support.ResourceStringUtils;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.runners.Parameterized.Parameters;
/**
* Ensures proper detection and removal of leading slash in strings.
*
* @author Florent Biville
*/
@RunWith(Parameterized.class)
public class ResourceStringUtilsTest {
final String actual;
final String expected;
final boolean hasText;
public ResourceStringUtilsTest(String testDescription, String actual, String expected, boolean hasText) {
this.actual = actual;
this.expected = expected;
this.hasText = hasText;
}
@Parameters(name = "{0}")
public static Collection<?> parameters() {
return Arrays.asList(new Object[][] {
{ "empty string has no text and should remain empty", "", "", false },
{ "blank string has no text and should remain as is", " ", " ", false },
{ "string made of only a leading slash has no text and should be returned empty", "/", "", false },
{ "blank string with only slashes has no text and should be returned as is", " / ", " / ", false },
{ "normal string has text and should be returned as such", "hello", "hello", true },
{ "normal string with leading slash has text and should be returned without leading slash", "/hello", "hello",
true }, });
}
@Test
public void shouldDetectTextPresence() {
assertThat(ResourceStringUtils.hasTextExceptSlash(actual), is(hasText));
}
@Test
public void shouldRemoveLeadingSlashIfAny() {
assertThat(ResourceStringUtils.removeLeadingSlash(actual), is(expected));
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2013 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.core.util;
import org.junit.Test;
/**
* @author Oliver Gierke
*/
public class FunctionTests {
@Test
public void foo() {
Foo foo = new Foo();
foo.apply(new Function<String, Integer>() {
@Override
public Integer apply(String input) {
return null;
}
});
}
static class Foo {
public Integer apply(Function<String, Integer> function) {
return null;
}
}
}

View File

@@ -0,0 +1,2 @@
field.name.required = Field {0}.{1} is required.
no.userid = {0}s must be assigned initial userids.

View File

@@ -1,17 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.data.rest" level="DEBUG"/>
<logger name="org.springframework.data" level="error" />
<root level="INFO">
<appender-ref ref="stdout"/>
</root>
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>