Added the ability to control the exporting of Repositories, query methods or entity fields by adding "exported = true|false" to the @RestResource annotation.

This commit is contained in:
Jon Brisbin
2012-05-09 09:47:52 -05:00
parent 29c839f5c6
commit e1e460e718
10 changed files with 93 additions and 31 deletions

View File

@@ -98,3 +98,34 @@ This would result in a link value of:
} ]
}
### Hiding certain Repositories, query methods, or fields
You may not want a certain Repository, a query method on a Repository, or a field of your entity to be exported at all. To tell the exporter to not export your these items, annotate them with `@RestResource` and set `exported = false`.
For example, to skip exporting a Repository:
@RestResource(exported = false)
public interface PersonRepository extends CrudRepository<Person, Long> {
}
To skip exporting a query method:
@RestResource(path = "people", rel = "people")
public interface PersonRepository extends CrudRepository<Person, Long> {
@RestResource(exported = false)
public List<Person> findByName(String name);
}
Or to skip exporting a field:
@Entity
@RestResource(exported = false)
public class Person {
@Id @GeneratedValue private Long id;
@OneToMany
@RestResource(exported = false)
private Map<String, Profile> profiles;
}

View File

@@ -104,6 +104,8 @@ If you have a JPA entity in your domain model that looks like...
...your PersonRepository will by default be declared in the ApplicationContext with a bean name of "personRepository". The web exporter will strip the word "Repository" from it and expose a resource named "person". The resulting URL of this repository (assuming the exporter webapp is deployed at context path `/data` in your servlet container) will be `http://localhost:8080/data/person`.
You can configure under what path, or whether a resource is exported at all, by using the `@RestResource` annotation. Details are here: [Configuring the REST URL path](../wiki/Configuring-the-REST-URL-path)
### Discoverability
The Web Exporter implements some aspects of the [HATEOS](http://en.wikipedia.org/wiki/HATEOAS) methodology. That means all the services of the web exporter are discoverable and exposed to the client using links.

View File

@@ -7,7 +7,6 @@ import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -124,19 +123,21 @@ public abstract class RepositoryExporter<M extends RepositoryMetadata<E>, E exte
repositories = new Repositories(applicationContext);
repositoryMetadata = new HashMap<String, M>();
for (Class<?> domainType : repositories) {
if (!exportOnlyTheseClasses.isEmpty() && !exportOnlyTheseClasses.contains(domainType.getName())) {
// Don't export this domain type
continue;
if (exportOnlyTheseClasses.isEmpty() || !exportOnlyTheseClasses.contains(domainType.getName())) {
Class<?> repoClass = repositories.getRepositoryInformationFor(domainType).getRepositoryInterface();
String name = StringUtils.uncapitalize(repoClass.getSimpleName().replaceAll("Repository", ""));
RestResource resourceAnno = repoClass.getAnnotation(RestResource.class);
boolean exported = true;
if (null != resourceAnno) {
if (StringUtils.hasText(resourceAnno.path())) {
name = resourceAnno.path();
}
exported = resourceAnno.exported();
}
if (exported) {
repositoryMetadata.put(name, createRepositoryMetadata(name, domainType, repoClass, repositories));
}
}
Class<?> repoClass = repositories.getRepositoryInformationFor(domainType).getRepositoryInterface();
String name;
RestResource pathSeg = repoClass.getAnnotation(RestResource.class);
if (null != pathSeg) {
name = pathSeg.path();
} else {
name = StringUtils.uncapitalize(repoClass.getSimpleName().replaceAll("Repository", ""));
}
repositoryMetadata.put(name, createRepositoryMetadata(name, domainType, repoClass, repositories));
}
}
}

View File

@@ -13,6 +13,7 @@ import java.lang.annotation.Target;
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Target({
ElementType.FIELD,
ElementType.METHOD,
ElementType.TYPE
})
@@ -20,7 +21,9 @@ import java.lang.annotation.Target;
@Inherited
public @interface RestResource {
String path();
boolean exported() default true;
String path() default "";
String rel() default "";

View File

@@ -1,5 +1,6 @@
package org.springframework.data.rest.repository.jpa;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.metamodel.Attribute;
@@ -9,6 +10,8 @@ import javax.persistence.metamodel.SingularAttribute;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.repository.EntityMetadata;
import org.springframework.data.rest.repository.annotation.RestResource;
import org.springframework.util.ReflectionUtils;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
@@ -30,15 +33,25 @@ public class JpaEntityMetadata implements EntityMetadata<JpaAttributeMetadata> {
}
for (Attribute attr : entityType.getAttributes()) {
Class<?> attrType = (attr instanceof PluralAttribute
? ((PluralAttribute) attr).getElementType().getJavaType()
: attr.getJavaType());
if (repositories.hasRepositoryFor(attrType)) {
linkedAttributes.put(attr.getName(), new JpaAttributeMetadata(entityType, attr));
} else {
if (!(attr instanceof SingularAttribute && ((SingularAttribute) attr).isId())
&& !(attr instanceof SingularAttribute && ((SingularAttribute) attr).isVersion())) {
embeddedAttributes.put(attr.getName(), new JpaAttributeMetadata(entityType, attr));
boolean exported = true;
Field field = ReflectionUtils.findField(type, attr.getJavaMember().getName());
if (null != field) {
RestResource fieldResourceAnno = field.getAnnotation(RestResource.class);
if (null != fieldResourceAnno) {
exported = fieldResourceAnno.exported();
}
}
if (exported) {
Class<?> attrType = (attr instanceof PluralAttribute
? ((PluralAttribute) attr).getElementType().getJavaType()
: attr.getJavaType());
if (repositories.hasRepositoryFor(attrType)) {
linkedAttributes.put(attr.getName(), new JpaAttributeMetadata(entityType, attr));
} else {
if (!(attr instanceof SingularAttribute && ((SingularAttribute) attr).isId())
&& !(attr instanceof SingularAttribute && ((SingularAttribute) attr).isVersion())) {
embeddedAttributes.put(attr.getName(), new JpaAttributeMetadata(entityType, attr));
}
}
}
}

View File

@@ -52,11 +52,17 @@ public class JpaRepositoryMetadata implements RepositoryMetadata<JpaEntityMetada
for (Method method : repositories.getRepositoryInformationFor(domainType).getQueryMethods()) {
String pathSeg = method.getName();
RestResource methodResourceAnno = method.getAnnotation(RestResource.class);
boolean methodExported = true;
if (null != methodResourceAnno) {
pathSeg = methodResourceAnno.path();
if (StringUtils.hasText(methodResourceAnno.path())) {
pathSeg = methodResourceAnno.path();
}
methodExported = methodResourceAnno.exported();
}
if (methodExported) {
ReflectionUtils.makeAccessible(method);
queryMethods.put(pathSeg, new RepositoryQueryMethod(method));
}
ReflectionUtils.makeAccessible(method);
queryMethods.put(pathSeg, new RepositoryQueryMethod(method));
}
Metamodel metamodel = entityManager.getMetamodel();

View File

@@ -6,6 +6,8 @@ import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import org.springframework.data.rest.repository.annotation.RestResource;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@@ -16,6 +18,7 @@ public class Family {
@GeneratedValue
private Long id;
private String surname;
@RestResource(exported = false)
@OneToMany
private List<Person> members;

View File

@@ -299,7 +299,9 @@ public class RepositoryRestController
URI path = buildUri(baseUri, repository, "search", entry.getKey());
RestResource resourceAnno = entry.getValue().method().getAnnotation(RestResource.class);
if (null != resourceAnno) {
path = buildUri(baseUri, repository, "search", resourceAnno.path());
if (StringUtils.hasText(resourceAnno.path())) {
path = buildUri(baseUri, repository, "search", resourceAnno.path());
}
if (StringUtils.hasText(resourceAnno.rel())) {
rel = repoMeta.rel() + "." + resourceAnno.rel();
}
@@ -1069,10 +1071,7 @@ public class RepositoryRestController
}
for (String attrName : entityMetadata.linkedAttributes().keySet()) {
URI uri = UriComponentsBuilder.fromUri(baseUri)
.pathSegment(attrName)
.build()
.toUri();
URI uri = buildUri(baseUri, attrName);
Link l = new SimpleLink(repoRel + "." + entity.getClass().getSimpleName() + "." + attrName, uri);
List<Link> links = (List<Link>) entityDto.get(LINKS);
if (null == links) {

View File

@@ -8,6 +8,8 @@ import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import org.springframework.data.rest.repository.annotation.RestResource;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/

View File

@@ -3,9 +3,11 @@ package org.springframework.data.rest.test.webmvc;
import java.util.UUID;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.repository.annotation.RestResource;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@RestResource(exported = false)
public interface UuidTestRepository extends CrudRepository<UuidTest, UUID> {
}