diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/Description.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/Description.java
index 67687da07..373cafeae 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/Description.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/Description.java
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ * 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.annotation;
import java.lang.annotation.ElementType;
@@ -6,10 +22,19 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
+ * Annotation to descibe semantics of a resource.
+ *
* @author Jon Brisbin
+ * @author Oliver Gierke
*/
-@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
+@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Description {
+
+ /**
+ * The textual description of the resource. Can be a resource bundle key for internationalization.
+ *
+ * @return
+ */
String value();
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/RepositoryRestResource.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/RepositoryRestResource.java
new file mode 100644
index 000000000..1002294d7
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/RepositoryRestResource.java
@@ -0,0 +1,75 @@
+/*
+ * 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.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotate a {@link org.springframework.data.repository.Repository} with this to customize export mapping and rels.
+ *
+ * @author Oliver Gierke
+ */
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface RepositoryRestResource {
+
+ /**
+ * Flag indicating whether this resource is exported at all.
+ *
+ * @return {@literal true} if the resource is to be exported, {@literal false} otherwise.
+ */
+ boolean exported() default true;
+
+ /**
+ * The path segment under which this resource is to be exported.
+ *
+ * @return A valid path segment.
+ */
+ String path() default "";
+
+ /**
+ * The rel value to use when generating links to the collection resource.
+ *
+ * @return A valid rel value.
+ */
+ String collectionResourceRel() default "";
+
+ /**
+ * The description of the collection resource.
+ *
+ * @return
+ */
+ Description collectionResourceDescription() default @Description(value = "");
+
+ /**
+ * The rel value to use when generating links to the item resource.
+ *
+ * @return A valid rel value.
+ */
+ String itemResourceRel() default "";
+
+ /**
+ * The description of the item resource.
+ *
+ * @return
+ */
+ Description itemResourceDescription() default @Description(value = "");
+}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/RestResource.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/RestResource.java
index 91666d38a..1387b416c 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/RestResource.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/annotation/RestResource.java
@@ -1,3 +1,18 @@
+/*
+ * 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.
+ * 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.annotation;
import java.lang.annotation.ElementType;
@@ -9,8 +24,12 @@ import java.lang.annotation.Target;
/**
* Annotate a {@link org.springframework.data.repository.Repository} with this to influence how it is exported and what
* the value of the {@literal rel} attribute will be in links.
+ *
+ * As of Spring Data REST 2.0, prefer using {@link RepositoryRestResource} to also be able to customize the relation
+ * type and description for the item resources exposed by the repository.
*
* @author Jon Brisbin
+ * @author Oliver Gierke
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@@ -38,4 +57,10 @@ public @interface RestResource {
*/
String rel() default "";
+ /**
+ * The description of the collection resource.
+ *
+ * @return
+ */
+ Description description() default @Description(value = "");
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/AnnotationBasedResourceDescription.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/AnnotationBasedResourceDescription.java
new file mode 100644
index 000000000..5d2f9674f
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/AnnotationBasedResourceDescription.java
@@ -0,0 +1,71 @@
+/*
+ * 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.core.mapping;
+
+import org.springframework.data.rest.core.annotation.Description;
+import org.springframework.http.MediaType;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * A {@link ResourceDescription} that is customized based on a {@link Description} annotation. Allows to fall back on
+ * another {@link ResourceDescription} to provide defaults.
+ *
+ * @author Oliver Gierke
+ */
+class AnnotationBasedResourceDescription extends ResolvableResourceDescriptionSupport {
+
+ private final String message;
+ private final ResourceDescription fallback;
+
+ /**
+ * Creates a new {@link AnnotationBasedResourceDescription} for the given {@link Description} and fallback.
+ *
+ * @param description must not be {@literal null}.
+ * @param fallback must not be {@literal null}.
+ */
+ AnnotationBasedResourceDescription(Description description, ResourceDescription fallback) {
+
+ Assert.notNull(description, "Description must not be null!");
+ Assert.notNull(fallback, "Fallback resource description must not be null!");
+
+ this.message = description.value();
+ this.fallback = fallback;
+ }
+
+ /**
+ * @return the message
+ */
+ public String getMessage() {
+ return StringUtils.hasText(message) ? message : fallback.getMessage();
+ }
+
+ /**
+ * @return the mediaType
+ */
+ public MediaType getType() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceDescription#isDefault()
+ */
+ @Override
+ public boolean isDefault() {
+ return !StringUtils.hasText(message);
+ }
+}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/CollectionResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/CollectionResourceMapping.java
index 4e719aa78..57b88d91a 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/CollectionResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/CollectionResourceMapping.java
@@ -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.
@@ -22,5 +22,17 @@ package org.springframework.data.rest.core.mapping;
*/
public interface CollectionResourceMapping extends ResourceMapping {
- String getSingleResourceRel();
+ /**
+ * Returns the relation type pointing to the item resource within a collection.
+ *
+ * @return
+ */
+ String getItemResourceRel();
+
+ /**
+ * Returns the {@link ResourceDescription} for the item resource.
+ *
+ * @return
+ */
+ ResourceDescription getItemResourceDescription();
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java
index 9f167982c..db6440f26 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java
@@ -111,7 +111,7 @@ class RepositoryAwareResourceInformation implements ResourceMetadata {
* @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#isExported()
*/
@Override
- public Boolean isExported() {
+ public boolean isExported() {
return mapping.isExported();
}
@@ -129,8 +129,8 @@ class RepositoryAwareResourceInformation implements ResourceMetadata {
* @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#getSingleResourceRel()
*/
@Override
- public String getSingleResourceRel() {
- return mapping.getSingleResourceRel();
+ public String getItemResourceRel() {
+ return mapping.getItemResourceRel();
}
/*
@@ -151,6 +151,24 @@ class RepositoryAwareResourceInformation implements ResourceMetadata {
return mapping.isPagingResource();
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#getDescription()
+ */
+ @Override
+ public ResourceDescription getDescription() {
+ return mapping.getDescription();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#getItemResourceDescription()
+ */
+ @Override
+ public ResourceDescription getItemResourceDescription() {
+ return mapping.getItemResourceDescription();
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.rest.core.mapping.ResourceMetadata#getSearchResourceMappings()
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMapping.java
index 54298b07e..69f79a79e 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMapping.java
@@ -17,9 +17,12 @@ package org.springframework.data.rest.core.mapping;
import java.lang.reflect.Modifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.rest.core.Path;
+import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.data.rest.core.support.RepositoriesUtils;
import org.springframework.hateoas.RelProvider;
@@ -37,9 +40,11 @@ import org.springframework.util.StringUtils;
*/
class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
- private final boolean EVO_INFLECTOR_IS_PRESENT = ClassUtils.isPresent("org.atteo.evo.inflector.English", null);
+ private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryCollectionResourceMapping.class);
+ private static final boolean EVO_INFLECTOR_IS_PRESENT = ClassUtils.isPresent("org.atteo.evo.inflector.English", null);
private final RestResource annotation;
+ private final RepositoryRestResource repositoryAnnotation;
private final CollectionResourceMapping domainTypeMapping;
private final boolean repositoryIsExportCandidate;
private final RepositoryMetadata metadata;
@@ -66,11 +71,18 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
Assert.notNull(relProvider, "RelProvider must not be null!");
this.annotation = AnnotationUtils.findAnnotation(repositoryType, RestResource.class);
+ this.repositoryAnnotation = AnnotationUtils.findAnnotation(repositoryType, RepositoryRestResource.class);
this.repositoryIsExportCandidate = Modifier.isPublic(repositoryType.getModifiers());
Class> domainType = RepositoriesUtils.getDomainType(repositoryType);
this.domainTypeMapping = EVO_INFLECTOR_IS_PRESENT ? new EvoInflectorTypeBasedCollectionResourceMapping(domainType,
relProvider) : new TypeBasedCollectionResourceMapping(domainType, relProvider);
+
+ if (annotation != null) {
+ LOGGER.warn(
+ "@RestResource detected to customize the repository resource for {}! Use @RepositoryRestResource instead!",
+ metadata.getRepositoryInterface().getName());
+ }
}
/*
@@ -80,8 +92,19 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
@Override
public Path getPath() {
- return annotation == null || !StringUtils.hasText(annotation.path()) ? domainTypeMapping.getPath() : new Path(
- annotation.path());
+ Path fallback = domainTypeMapping.getPath();
+
+ if (repositoryAnnotation != null) {
+ String path = repositoryAnnotation.path();
+ return StringUtils.hasText(path) ? new Path(path) : fallback;
+ }
+
+ if (annotation != null) {
+ String path = annotation.path();
+ return StringUtils.hasText(path) ? new Path(path) : fallback;
+ }
+
+ return fallback;
}
/*
@@ -90,7 +113,20 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
*/
@Override
public String getRel() {
- return annotation == null || !StringUtils.hasText(annotation.rel()) ? domainTypeMapping.getRel() : annotation.rel();
+
+ String fallback = domainTypeMapping.getRel();
+
+ if (repositoryAnnotation != null) {
+ String rel = repositoryAnnotation.collectionResourceRel();
+ return StringUtils.hasText(rel) ? rel : fallback;
+ }
+
+ if (annotation != null) {
+ String rel = annotation.rel();
+ return StringUtils.hasText(rel) ? rel : fallback;
+ }
+
+ return fallback;
}
/*
@@ -98,8 +134,16 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
* @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#getSingleResourceRel()
*/
@Override
- public String getSingleResourceRel() {
- return domainTypeMapping.getSingleResourceRel();
+ public String getItemResourceRel() {
+
+ String fallback = domainTypeMapping.getItemResourceRel();
+
+ if (repositoryAnnotation != null) {
+ String rel = repositoryAnnotation.itemResourceRel();
+ return StringUtils.hasText(rel) ? rel : fallback;
+ }
+
+ return fallback;
}
/*
@@ -107,8 +151,17 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
* @see org.springframework.data.rest.core.mapping.ResourceMapping#isExported()
*/
@Override
- public Boolean isExported() {
- return annotation == null ? repositoryIsExportCandidate && domainTypeMapping.isExported() : annotation.exported();
+ public boolean isExported() {
+
+ if (repositoryAnnotation != null) {
+ return repositoryAnnotation.exported();
+ }
+
+ if (annotation != null) {
+ return annotation.exported();
+ }
+
+ return repositoryIsExportCandidate && domainTypeMapping.isExported();
}
/*
@@ -119,4 +172,36 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
public boolean isPagingResource() {
return metadata.isPagingRepository();
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#getDescription()
+ */
+ @Override
+ public ResourceDescription getDescription() {
+
+ ResourceDescription fallback = SimpleResourceDescription.defaultFor(getRel());
+
+ if (repositoryAnnotation != null) {
+ return new AnnotationBasedResourceDescription(repositoryAnnotation.collectionResourceDescription(), fallback);
+ }
+
+ return fallback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#getItemResourceDescription()
+ */
+ @Override
+ public ResourceDescription getItemResourceDescription() {
+
+ ResourceDescription fallback = SimpleResourceDescription.defaultFor(getItemResourceRel());
+
+ if (repositoryAnnotation != null) {
+ return new AnnotationBasedResourceDescription(repositoryAnnotation.itemResourceDescription(), fallback);
+ }
+
+ return fallback;
+ }
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMapping.java
index b9dcc3fb0..b75efb524 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMapping.java
@@ -90,7 +90,7 @@ class RepositoryMethodResourceMapping implements MethodResourceMapping {
* @see org.springframework.data.rest.core.mapping.ResourceMapping#isExported()
*/
@Override
- public Boolean isExported() {
+ public boolean isExported() {
return isExported;
}
@@ -138,4 +138,13 @@ class RepositoryMethodResourceMapping implements MethodResourceMapping {
public boolean isPagingResource() {
return paging;
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#getDescription()
+ */
+ @Override
+ public ResourceDescription getDescription() {
+ return null;
+ }
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResolvableResourceDescriptionSupport.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResolvableResourceDescriptionSupport.java
new file mode 100644
index 000000000..bda50c0ca
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResolvableResourceDescriptionSupport.java
@@ -0,0 +1,50 @@
+/*
+ * 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.core.mapping;
+
+/**
+ *
+ * @author Oliver Gierke
+ */
+public abstract class ResolvableResourceDescriptionSupport implements ResourceDescription {
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.context.MessageSourceResolvable#getCodes()
+ */
+ @Override
+ public String[] getCodes() {
+ return new String[] { getMessage() };
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.context.MessageSourceResolvable#getArguments()
+ */
+ @Override
+ public Object[] getArguments() {
+ return new Object[0];
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.context.MessageSourceResolvable#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage() {
+ return null;
+ }
+}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceDescription.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceDescription.java
new file mode 100644
index 000000000..3463e45aa
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceDescription.java
@@ -0,0 +1,45 @@
+/*
+ * 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.core.mapping;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.MessageSourceResolvable;
+import org.springframework.http.MediaType;
+
+/**
+ * A description of a resource. Resolvable to plain text by using a {@link MessageSource}.
+ *
+ * @author Oliver Gierke
+ */
+public interface ResourceDescription extends MessageSourceResolvable {
+
+ /**
+ * Returns the description. This can be a message source code or a custom text format. Prefer resolving the
+ * {@link ResourceDescription} using a {@link MessageSource}.
+ *
+ * @return
+ */
+ String getMessage();
+
+ /**
+ * Returns whether this is the default description.
+ *
+ * @return
+ */
+ boolean isDefault();
+
+ MediaType getType();
+}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMapping.java
index 32132332b..bfb869dbe 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMapping.java
@@ -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.
@@ -29,7 +29,7 @@ public interface ResourceMapping {
*
* @return will never be {@literal null}.
*/
- Boolean isExported();
+ boolean isExported();
/**
* Returns the relation for the resource exported.
@@ -51,4 +51,11 @@ public interface ResourceMapping {
* @return
*/
boolean isPagingResource();
+
+ /**
+ * Returns the resource's description.
+ *
+ * @return
+ */
+ ResourceDescription getDescription();
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java
index 8ba7dcee2..2e85dff85 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java
@@ -26,6 +26,7 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.Path;
+import org.springframework.data.rest.core.annotation.Description;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.support.RepositoriesUtils;
@@ -222,7 +223,8 @@ public class ResourceMappings implements Iterable {
}
ResourceMetadata propertyTypeMapping = getMappingFor(property.getActualType());
- propertyMapping = new PersistentPropertyResourceMapping(property, propertyTypeMapping);
+ ResourceMetadata ownerTypeMapping = getMappingFor(property.getOwner().getType());
+ propertyMapping = new PersistentPropertyResourceMapping(property, propertyTypeMapping, ownerTypeMapping);
propertyCache.put(property, propertyMapping);
@@ -257,7 +259,9 @@ public class ResourceMappings implements Iterable {
private final PersistentProperty> property;
private final ResourceMapping typeMapping;
+ private final CollectionResourceMapping ownerTypeMapping;
private final RestResource annotation;
+ private final Description description;
/**
* Creates a new {@link PersistentPropertyResourceMapping}.
@@ -265,12 +269,16 @@ public class ResourceMappings implements Iterable {
* @param property must not be {@literal null}.
* @param exported whether the property is exported or not.
*/
- public PersistentPropertyResourceMapping(PersistentProperty> property, ResourceMapping typeMapping) {
+ public PersistentPropertyResourceMapping(PersistentProperty> property, ResourceMapping typeMapping,
+ CollectionResourceMapping ownerTypeMapping) {
Assert.notNull(property, "PersistentProperty must not be null!");
+
this.property = property;
this.typeMapping = typeMapping;
- this.annotation = property.findAnnotation(RestResource.class);
+ this.ownerTypeMapping = ownerTypeMapping;
+ this.annotation = property.isAssociation() ? property.findAnnotation(RestResource.class) : null;
+ this.description = property.findAnnotation(Description.class);
}
/*
@@ -297,7 +305,7 @@ public class ResourceMappings implements Iterable {
* @see org.springframework.data.rest.core.mapping.ResourceMapping#isExported()
*/
@Override
- public Boolean isExported() {
+ public boolean isExported() {
if (typeMapping == null) {
return false;
@@ -314,5 +322,26 @@ public class ResourceMappings implements Iterable {
public boolean isPagingResource() {
return false;
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#getDescription()
+ */
+ @Override
+ public ResourceDescription getDescription() {
+
+ ResourceDescription fallback = SimpleResourceDescription.defaultFor(property,
+ ownerTypeMapping.getItemResourceRel());
+
+ if (description != null) {
+ return new AnnotationBasedResourceDescription(description, fallback);
+ }
+
+ if (annotation != null) {
+ return new AnnotationBasedResourceDescription(annotation.description(), fallback);
+ }
+
+ return fallback;
+ }
}
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SearchResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SearchResourceMappings.java
index f859ed8d8..9497c02fb 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SearchResourceMappings.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SearchResourceMappings.java
@@ -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.
@@ -100,7 +100,7 @@ public class SearchResourceMappings implements Iterable,
* @see org.springframework.data.rest.core.mapping.ResourceMapping#isExported()
*/
@Override
- public Boolean isExported() {
+ public boolean isExported() {
return !mappings.isEmpty();
}
@@ -113,6 +113,15 @@ public class SearchResourceMappings implements Iterable,
return false;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#getDescription()
+ */
+ @Override
+ public ResourceDescription getDescription() {
+ return null;
+ }
+
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SimpleResourceDescription.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SimpleResourceDescription.java
new file mode 100644
index 000000000..0fef09276
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SimpleResourceDescription.java
@@ -0,0 +1,72 @@
+/*
+ * 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.core.mapping;
+
+import org.springframework.data.mapping.PersistentProperty;
+import org.springframework.http.MediaType;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Oliver Gierke
+ */
+public class SimpleResourceDescription extends ResolvableResourceDescriptionSupport {
+
+ private static final String DEFAULT_KEY_PREFIX = "rest.description";
+ private static final MediaType DEFAULT_MEDIA_TYPE = MediaType.TEXT_PLAIN;
+
+ private String message;
+ private MediaType type;
+
+ private SimpleResourceDescription(String message, MediaType mediaType) {
+ this.message = message;
+ this.type = mediaType;
+ }
+
+ public static ResourceDescription defaultFor(PersistentProperty> property, String rel) {
+
+ String message = String.format("%s.%s.%s", DEFAULT_KEY_PREFIX, rel, property.getName());
+ return new SimpleResourceDescription(message, DEFAULT_MEDIA_TYPE);
+ }
+
+ public static ResourceDescription defaultFor(String rel) {
+
+ String message = String.format("%s.%s", DEFAULT_KEY_PREFIX, rel);
+ return new SimpleResourceDescription(message, DEFAULT_MEDIA_TYPE);
+ }
+
+ public static ResourceDescription defaultForCollection(Class> type) {
+ return null;
+ }
+
+ public static ResourceDescription defaultForMethod(RepositoryMethodResourceMapping mapping) {
+ return null;
+ }
+
+ /**
+ * @return the message
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ public MediaType getType() {
+ return type;
+ }
+
+ public boolean isDefault() {
+ return StringUtils.hasText(message) && message.startsWith(DEFAULT_KEY_PREFIX);
+ }
+}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java
index d9ef57e14..c14c0a847 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java
@@ -19,6 +19,7 @@ import java.lang.reflect.Modifier;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.rest.core.Path;
+import org.springframework.data.rest.core.annotation.Description;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.core.EvoInflectorRelProvider;
@@ -34,8 +35,9 @@ import org.springframework.util.StringUtils;
class TypeBasedCollectionResourceMapping implements CollectionResourceMapping {
private final Class> type;
- private final RestResource annotation;
private final RelProvider relProvider;
+ private final RestResource annotation;
+ private final Description description;
/**
* Creates a new {@link TypeBasedCollectionResourceMapping} using the given type.
@@ -60,6 +62,7 @@ class TypeBasedCollectionResourceMapping implements CollectionResourceMapping {
this.type = type;
this.relProvider = relProvider;
this.annotation = AnnotationUtils.findAnnotation(type, RestResource.class);
+ this.description = AnnotationUtils.findAnnotation(type, Description.class);
}
/*
@@ -79,7 +82,7 @@ class TypeBasedCollectionResourceMapping implements CollectionResourceMapping {
* @see org.springframework.data.rest.core.mapping.ResourceMapping#isExported()
*/
@Override
- public Boolean isExported() {
+ public boolean isExported() {
return annotation == null ? Modifier.isPublic(type.getModifiers()) : annotation.exported();
}
@@ -102,7 +105,7 @@ class TypeBasedCollectionResourceMapping implements CollectionResourceMapping {
* @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#getSingleResourceRel()
*/
@Override
- public String getSingleResourceRel() {
+ public String getItemResourceRel() {
return relProvider.getSingleResourceRelFor(type);
}
@@ -115,6 +118,38 @@ class TypeBasedCollectionResourceMapping implements CollectionResourceMapping {
return false;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#getDescription()
+ */
+ @Override
+ public ResourceDescription getDescription() {
+
+ ResourceDescription fallback = SimpleResourceDescription.defaultFor(getRel());
+
+ return fallback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#getItemResourceDescription()
+ */
+ @Override
+ public ResourceDescription getItemResourceDescription() {
+
+ ResourceDescription fallback = SimpleResourceDescription.defaultFor(getItemResourceRel());
+
+ if (annotation != null && StringUtils.hasText(annotation.description().value())) {
+ return new AnnotationBasedResourceDescription(annotation.description(), fallback);
+ }
+
+ if (description != null) {
+ return new AnnotationBasedResourceDescription(description, fallback);
+ }
+
+ return fallback;
+ }
+
/**
* Returns the default path to be used if the path is not configured manually.
*
@@ -122,6 +157,10 @@ class TypeBasedCollectionResourceMapping implements CollectionResourceMapping {
* @return
*/
protected String getDefaultPathFor(Class> type) {
+ return getSimpleTypeName(type);
+ }
+
+ private String getSimpleTypeName(Class> type) {
return StringUtils.uncapitalize(type.getSimpleName());
}
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/RepositoryRelProvider.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/RepositoryRelProvider.java
index b80a3650b..9376f92dd 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/RepositoryRelProvider.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/RepositoryRelProvider.java
@@ -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.
@@ -17,8 +17,11 @@ package org.springframework.data.rest.core.support;
import org.springframework.data.rest.core.mapping.ResourceMappings;
import org.springframework.hateoas.RelProvider;
+import org.springframework.util.Assert;
/**
+ * A {@link RelProvider} based on the {@link ResourceMappings} for the registered repositories.
+ *
* @author Oliver Gierke
*/
public class RepositoryRelProvider implements RelProvider {
@@ -26,11 +29,13 @@ public class RepositoryRelProvider implements RelProvider {
private final ResourceMappings mappings;
/**
- * @param repositories
- * @param config
+ * Creates a new {@link RepositoryRelProvider} for the given {@link ResourceMappings}.
+ *
+ * @param mappings must not be {@literal null}.
*/
public RepositoryRelProvider(ResourceMappings mappings) {
+ Assert.notNull(mappings, "ResourceMappings must not be null!");
this.mappings = mappings;
}
@@ -49,7 +54,7 @@ public class RepositoryRelProvider implements RelProvider {
*/
@Override
public String getSingleResourceRelFor(Class> type) {
- return mappings.getMappingFor(type).getSingleResourceRel();
+ return mappings.getMappingFor(type).getItemResourceRel();
}
/*
diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/jpa/PersonRepository.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/jpa/PersonRepository.java
index 98773b638..f346f7d28 100644
--- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/jpa/PersonRepository.java
+++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/jpa/PersonRepository.java
@@ -1,3 +1,18 @@
+/*
+ * 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.
+ * 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 java.util.Date;
@@ -7,6 +22,7 @@ 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.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
@@ -15,8 +31,9 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
* A repository to manage {@link Person}s.
*
* @author Jon Brisbin
+ * @author Oliver Gierke
*/
-@RestResource(rel = "people", path = "people")
+@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository {
@RestResource(rel = "firstname", path = "firstname")
@@ -29,9 +46,9 @@ public interface PersonRepository extends PagingAndSortingRepository persistentEntity = mappingContext.getPersistentEntity(Entity.class);
-
- @Mock ResourceMapping typeMapping;
-
- @Before
- public void setUp() {
- when(typeMapping.isExported()).thenReturn(true);
- }
/**
* @see DATAREST-175
@@ -57,13 +48,12 @@ public class PersistentPropertyResourceMappingUnitTests {
@Test
public void usesPropertyNameAsDefaultResourceMappingRelAndPath() {
- MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty("first");
- ResourceMapping propertyMapping = new PersistentPropertyResourceMapping(persistentProperty, typeMapping);
+ ResourceMapping mapping = getPropertyMappingFor(Entity.class, "first");
- assertThat(propertyMapping, is(notNullValue()));
- assertThat(propertyMapping.getPath(), is(new Path("first")));
- assertThat(propertyMapping.getRel(), is("first"));
- assertThat(propertyMapping.isExported(), is(true));
+ assertThat(mapping, is(notNullValue()));
+ assertThat(mapping.getPath(), is(new Path("first")));
+ assertThat(mapping.getRel(), is("first"));
+ assertThat(mapping.isExported(), is(true));
}
/**
@@ -72,13 +62,12 @@ public class PersistentPropertyResourceMappingUnitTests {
@Test
public void considersMappingAnnotationOnDomainClassProperty() {
- MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty("second");
- ResourceMapping propertyMapping = new PersistentPropertyResourceMapping(persistentProperty, typeMapping);
+ ResourceMapping mapping = getPropertyMappingFor(Entity.class, "second");
- assertThat(propertyMapping, is(notNullValue()));
- assertThat(propertyMapping.getPath(), is(new Path("secPath")));
- assertThat(propertyMapping.getRel(), is("secRel"));
- assertThat(propertyMapping.isExported(), is(false));
+ assertThat(mapping, is(notNullValue()));
+ assertThat(mapping.getPath(), is(new Path("secPath")));
+ assertThat(mapping.getRel(), is("secRel"));
+ assertThat(mapping.isExported(), is(false));
}
/**
@@ -87,29 +76,67 @@ public class PersistentPropertyResourceMappingUnitTests {
@Test
public void considersMappingAnnotationOnDomainClassPropertyMethod() {
- MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty("third");
- ResourceMapping propertyMapping = new PersistentPropertyResourceMapping(persistentProperty, typeMapping);
+ ResourceMapping mapping = getPropertyMappingFor(Entity.class, "third");
- assertThat(propertyMapping, is(notNullValue()));
- assertThat(propertyMapping.getPath(), is(new Path("thirdPath")));
- assertThat(propertyMapping.getRel(), is("thirdRel"));
- assertThat(propertyMapping.isExported(), is(false));
+ assertThat(mapping, is(notNullValue()));
+ assertThat(mapping.getPath(), is(new Path("thirdPath")));
+ assertThat(mapping.getRel(), is("thirdRel"));
+ assertThat(mapping.isExported(), is(false));
}
- static class Entity {
+ @Test
+ public void returnsDefaultDescriptionKey() {
- Related first, third;
+ ResourceMapping mapping = getPropertyMappingFor(Entity.class, "second");
+ ResourceDescription description = mapping.getDescription();
+
+ assertThat(description.isDefault(), is(true));
+ assertThat(description.getMessage(), is("rest.description.entity.second"));
+ }
+
+ /**
+ * @see DATAREST-???
+ */
+ @Test
+ public void considersAtDescription() {
+
+ ResourceMapping mapping = getPropertyMappingFor(Entity.class, "fourth");
+
+ ResourceDescription description = mapping.getDescription();
+ assertThat(description.isDefault(), is(false));
+ assertThat(description.getMessage(), is("Some description"));
+ }
+
+ private ResourceMapping getPropertyMappingFor(Class> entity, String propertyName) {
+
+ MongoPersistentEntity> persistentEntity = mappingContext.getPersistentEntity(entity);
+ MongoPersistentProperty property = persistentEntity.getPersistentProperty(propertyName);
+
+ CollectionResourceMapping entityResourceMapping = new TypeBasedCollectionResourceMapping(entity);
+ ResourceMapping propertyTypeMapping = new TypeBasedCollectionResourceMapping(property.getType());
+
+ return new PersistentPropertyResourceMapping(property, propertyTypeMapping, entityResourceMapping);
+ }
+
+ public static class Entity {
+
+ Related first;
+ @DBRef Related third;
+
+ @DBRef//
@RestResource(path = "secPath", rel = "secRel", exported = false)//
List second;
+ @Description("Some description") String fourth;
+
@RestResource(path = "thirdPath", rel = "thirdRel", exported = false)
public Related getThird() {
return third;
}
}
- static class Related {
+ public static class Related {
}
}
diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMappingUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMappingUnitTests.java
index df7973096..184896f1c 100644
--- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMappingUnitTests.java
+++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMappingUnitTests.java
@@ -25,6 +25,7 @@ import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.rest.core.Path;
+import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
/**
@@ -41,7 +42,7 @@ public class RepositoryCollectionResourceMappingUnitTests {
assertThat(mapping.getPath(), is(new Path("persons")));
assertThat(mapping.getRel(), is("persons"));
- assertThat(mapping.getSingleResourceRel(), is("person"));
+ assertThat(mapping.getItemResourceRel(), is("person"));
assertThat(mapping.isExported(), is(true));
}
@@ -52,7 +53,7 @@ public class RepositoryCollectionResourceMappingUnitTests {
assertThat(mapping.getPath(), is(new Path("bar")));
assertThat(mapping.getRel(), is("foo"));
- assertThat(mapping.getSingleResourceRel(), is("annotatedPerson"));
+ assertThat(mapping.getItemResourceRel(), is("annotatedPerson"));
assertThat(mapping.isExported(), is(false));
}
@@ -63,7 +64,7 @@ public class RepositoryCollectionResourceMappingUnitTests {
assertThat(mapping.getPath(), is(new Path("/trumpsAll")));
assertThat(mapping.getRel(), is("foo"));
- assertThat(mapping.getSingleResourceRel(), is("annotatedPerson"));
+ assertThat(mapping.getItemResourceRel(), is("annotatedPerson"));
assertThat(mapping.isExported(), is(true));
}
@@ -82,6 +83,14 @@ public class RepositoryCollectionResourceMappingUnitTests {
assertThat(getResourceMappingFor(PersonRepository.class).isPagingResource(), is(true));
}
+ @Test
+ public void discoversCustomizationsUsingRestRepositoryResource() {
+
+ CollectionResourceMapping mapping = getResourceMappingFor(RepositoryAnnotatedRepository.class);
+ assertThat(mapping.getRel(), is("foo"));
+ assertThat(mapping.getItemResourceRel(), is("bar"));
+ }
+
private static CollectionResourceMapping getResourceMappingFor(Class> repositoryInterface) {
RepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryInterface);
@@ -106,4 +115,7 @@ public class RepositoryCollectionResourceMappingUnitTests {
public static class PublicClass {}
interface PackageProtectedRepository extends Repository {}
+
+ @RepositoryRestResource(collectionResourceRel = "foo", itemResourceRel = "bar")
+ interface RepositoryAnnotatedRepository extends Repository {}
}
diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTest.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTests.java
similarity index 99%
rename from spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTest.java
rename to spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTests.java
index 68967fd46..d48aeba4f 100644
--- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTest.java
+++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTests.java
@@ -48,7 +48,7 @@ import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JpaRepositoryConfig.class)
@Transactional
-public class ResourceMappingsIntegrationTest {
+public class ResourceMappingsIntegrationTests {
@Autowired ListableBeanFactory factory;
diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMappingUnitTest.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMappingUnitTests.java
similarity index 70%
rename from spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMappingUnitTest.java
rename to spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMappingUnitTests.java
index 5077d594a..34fc0ed53 100644
--- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMappingUnitTest.java
+++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMappingUnitTests.java
@@ -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.
@@ -21,15 +21,13 @@ 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 {
+public class TypeBasedCollectionResourceMappingUnitTests {
@Test
public void defaultsMappingsByType() {
@@ -38,7 +36,7 @@ public class TypeBasedCollectionResourceMappingUnitTest {
assertThat(mapping.getPath(), is(new Path("sample")));
assertThat(mapping.getRel(), is("samples"));
- assertThat(mapping.getSingleResourceRel(), is("sample"));
+ assertThat(mapping.getItemResourceRel(), is("sample"));
assertThat(mapping.isExported(), is(true));
}
@@ -49,7 +47,7 @@ public class TypeBasedCollectionResourceMappingUnitTest {
assertThat(mapping.getPath(), is(new Path("customizedSample")));
assertThat(mapping.getRel(), is("myRel"));
- assertThat(mapping.getSingleResourceRel(), is("customizedSample"));
+ assertThat(mapping.getItemResourceRel(), is("customizedSample"));
assertThat(mapping.isExported(), is(true));
}
@@ -64,6 +62,24 @@ public class TypeBasedCollectionResourceMappingUnitTest {
assertThat(mapping.isExported(), is(false));
}
+ /**
+ * @see
+ */
+ @Test
+ public void usesDefaultDescriptionIfNoAnnotationPresent() {
+
+ CollectionResourceMapping mapping = new TypeBasedCollectionResourceMapping(Sample.class);
+ ResourceDescription description = mapping.getDescription();
+
+ assertThat(description.isDefault(), is(true));
+ assertThat(description.getMessage(), is("rest.description.samples"));
+
+ ResourceDescription itemDescription = mapping.getItemResourceDescription();
+
+ assertThat(itemDescription.isDefault(), is(true));
+ assertThat(itemDescription.getMessage(), is("rest.description.sample"));
+ }
+
public interface Sample {}
interface HiddenSample {}
diff --git a/spring-data-rest-core/src/test/resources/logback.xml b/spring-data-rest-core/src/test/resources/logback.xml
index ad5cbef50..f1b93ebdd 100644
--- a/spring-data-rest-core/src/test/resources/logback.xml
+++ b/spring-data-rest-core/src/test/resources/logback.xml
@@ -7,7 +7,7 @@
-
+
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java
index 4cec36e8f..ec52a80fd 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java
@@ -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.
@@ -200,7 +200,7 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi
ResourceMetadata repoMapping = repoRequest.getResourceMetadata();
Link selfLink = resource.getLink("self");
- String rel = repoMapping.getSingleResourceRel();
+ String rel = repoMapping.getItemResourceRel();
return new Link(selfLink.getHref(), rel);
}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java
index 5092d57be..2ee349131 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java
@@ -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.
@@ -30,6 +30,8 @@ import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.Lazy;
+import org.springframework.context.support.MessageSourceAccessor;
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.data.repository.support.Repositories;
@@ -39,6 +41,7 @@ import org.springframework.data.rest.core.event.AnnotatedHandlerBeanPostProcesso
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.data.rest.core.invoke.DefaultRepositoryInvokerFactory;
import org.springframework.data.rest.core.invoke.RepositoryInvokerFactory;
+import org.springframework.data.rest.core.mapping.ResourceDescription;
import org.springframework.data.rest.core.mapping.ResourceMappings;
import org.springframework.data.rest.core.support.DomainObjectMerger;
import org.springframework.data.rest.core.util.UUIDConverter;
@@ -268,7 +271,23 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
*/
@Bean
public PersistentEntityToJsonSchemaConverter jsonSchemaConverter() {
- return new PersistentEntityToJsonSchemaConverter(repositories(), resourceMappings());
+ return new PersistentEntityToJsonSchemaConverter(repositories(), resourceMappings(),
+ resourceDescriptionMessageSourceAccessor());
+ }
+
+ /**
+ * The {@link MessageSourceAccessor} to provide messages for {@link ResourceDescription}s being rendered.
+ *
+ * @return
+ */
+ @Bean
+ public MessageSourceAccessor resourceDescriptionMessageSourceAccessor() {
+
+ ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
+ messageSource.setBasename("classpath:rest-messages");
+ messageSource.setUseCodeAsDefaultMessage(true);
+
+ return new MessageSourceAccessor(messageSource);
}
/**
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JsonSchema.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JsonSchema.java
index 5b1fab7a5..19a239d76 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JsonSchema.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JsonSchema.java
@@ -1,3 +1,18 @@
+/*
+ * 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.
+ * 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.json;
import java.util.ArrayList;
@@ -5,16 +20,20 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.hateoas.Resource;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
/**
+ * Model class to render JSON schema documents.
+ *
* @author Jon Brisbin
+ * @author Oliver Gierke
*/
public class JsonSchema extends Resource