DATAREST-233 - Introduced @RepositoryRestResource.
@RepositoryRestResource exposes more detailed attributes tailored to the use case of exposing a repository. @RestResource is still recognized on repository interfaces but we now issue a warning and indicate the new annotation to be used. Introduced a minimal ResourceDescription interface and let @Description be used within @RestResource and @RepositoryRestResource. We now generate default resource bundle keys and resolve them against a "rest-messages" resource bundle by default. JsonSchema converter now uses the rendered descriptions for schema descriptions.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<Map<String, JsonSchema.Property>> {
|
||||
|
||||
private final String name;
|
||||
@SuppressWarnings("unused") private final String description;
|
||||
private final String description;
|
||||
|
||||
public JsonSchema(String name, String description) {
|
||||
super(new HashMap<String, Property>());
|
||||
@@ -26,6 +45,10 @@ public class JsonSchema extends Resource<Map<String, JsonSchema.Property>> {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@JsonProperty("properties")
|
||||
@Override
|
||||
public Map<String, JsonSchema.Property> getContent() {
|
||||
@@ -46,6 +69,7 @@ public class JsonSchema extends Resource<Map<String, JsonSchema.Property>> {
|
||||
}
|
||||
|
||||
public static class Property {
|
||||
|
||||
private final String type;
|
||||
private final String description;
|
||||
private final boolean required;
|
||||
|
||||
@@ -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 static org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.*;
|
||||
@@ -10,6 +25,7 @@ import java.util.Set;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.data.mapping.Association;
|
||||
@@ -18,9 +34,12 @@ import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.SimpleAssociationHandler;
|
||||
import org.springframework.data.mapping.SimplePropertyHandler;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.core.annotation.Description;
|
||||
import org.springframework.data.rest.core.mapping.ResourceDescription;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMapping;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.json.JsonSchema.ArrayProperty;
|
||||
import org.springframework.data.rest.webmvc.json.JsonSchema.Property;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryLinkBuilder;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -37,6 +56,7 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric
|
||||
private final Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
|
||||
private final ResourceMappings mappings;
|
||||
private final Repositories repositories;
|
||||
private final MessageSourceAccessor accessor;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PersistentEntityToJsonSchemaConverter} for the given {@link Repositories} and
|
||||
@@ -44,14 +64,17 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric
|
||||
*
|
||||
* @param repositories must not be {@literal null}.
|
||||
* @param mappings must not be {@literal null}.
|
||||
* @param accessor
|
||||
*/
|
||||
public PersistentEntityToJsonSchemaConverter(Repositories repositories, ResourceMappings mappings) {
|
||||
public PersistentEntityToJsonSchemaConverter(Repositories repositories, ResourceMappings mappings,
|
||||
MessageSourceAccessor accessor) {
|
||||
|
||||
Assert.notNull(repositories, "Repositories must not be null!");
|
||||
Assert.notNull(mappings, "ResourceMappings must not be null!");
|
||||
|
||||
this.repositories = repositories;
|
||||
this.mappings = mappings;
|
||||
this.accessor = accessor;
|
||||
|
||||
for (Class<?> domainType : repositories) {
|
||||
convertiblePairs.add(new ConvertiblePair(domainType, JsonSchema.class));
|
||||
@@ -90,9 +113,8 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric
|
||||
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity((Class<?>) source);
|
||||
final ResourceMetadata metadata = mappings.getMappingFor(persistentEntity.getType());
|
||||
String entityDesc = persistentEntity.getType().isAnnotationPresent(Description.class) ? persistentEntity.getType()
|
||||
.getAnnotation(Description.class).value() : null;
|
||||
final JsonSchema jsonSchema = new JsonSchema(persistentEntity.getName(), entityDesc);
|
||||
final JsonSchema jsonSchema = new JsonSchema(persistentEntity.getName(), accessor.getMessage(metadata
|
||||
.getItemResourceDescription()));
|
||||
|
||||
persistentEntity.doWithProperties(new SimplePropertyHandler() {
|
||||
|
||||
@@ -106,12 +128,16 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric
|
||||
Class<?> propertyType = persistentProperty.getType();
|
||||
String type = uncapitalize(propertyType.getSimpleName());
|
||||
|
||||
boolean notNull = persistentProperty.isAnnotationPresent(NotNull.class);
|
||||
Description descriptionAnnotation = persistentProperty.findAnnotation(Description.class);
|
||||
String desc = descriptionAnnotation == null ? null : descriptionAnnotation.value();
|
||||
ResourceMapping propertyMapping = metadata.getMappingFor(persistentProperty);
|
||||
|
||||
boolean notNull = persistentProperty.isAnnotationPresent(NotNull.class);
|
||||
ResourceDescription description = propertyMapping.getDescription();
|
||||
String message = accessor.getMessage(description);
|
||||
|
||||
Property property = persistentProperty.isCollectionLike() ? //
|
||||
new ArrayProperty("array", message, notNull)
|
||||
: new Property(type, message, notNull);
|
||||
|
||||
JsonSchema.Property property = persistentProperty.isCollectionLike() ? new JsonSchema.ArrayProperty("array",
|
||||
desc, notNull) : new JsonSchema.Property(type, desc, notNull);
|
||||
jsonSchema.addProperty(persistentProperty.getName(), property);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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.support;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -112,6 +127,6 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
public Link linkToSingleResource(Class<?> type, Object id) {
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(type);
|
||||
return linkFor(type).slash(id).withRel(metadata.getSingleResourceRel());
|
||||
return linkFor(type).slash(id).withRel(metadata.getItemResourceRel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
@@ -30,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<Person, Long> {
|
||||
|
||||
@RestResource(rel = "firstname", path = "firstname")
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.webmvc.json;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.rest.webmvc.AbstractControllerIntegrationTests;
|
||||
import org.springframework.data.rest.webmvc.mongodb.MongoDbRepositoryConfig;
|
||||
import org.springframework.data.rest.webmvc.mongodb.Profile;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@ContextConfiguration(classes = MongoDbRepositoryConfig.class)
|
||||
public class PersistentEntityToJsonSchemaConverterUnitTests extends AbstractControllerIntegrationTests {
|
||||
|
||||
@Autowired PersistentEntityToJsonSchemaConverter converter;
|
||||
|
||||
@Test
|
||||
public void addsDescriptionToSchemaRoot() {
|
||||
|
||||
JsonSchema schema = converter.convert(Profile.class);
|
||||
|
||||
assertThat(schema.getDescription(), is("Profile description"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
rest.description.profile=Profile description
|
||||
Reference in New Issue
Block a user