DATAMONGO-1835 - Add support for JSON Schema.
We now can create a $jsonSchema that can be used as a validator when creating collections and as predicate for queries. Required fields and properties get mapped according to the @Field annotation on domain objects.
MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname")
.properties(string("firstname").possibleValues("luke", "han"),
object("address").properties(string("postCode").minLength(4).maxLength(5)))
.build();
resulting in the following schema:
{
"type": "object",
"required": [ "firstname", "lastname" ],
"properties": {
"firstname": {
"type": "string", "enum": [ "luke", "han" ],
},
"address": {
"type": "object",
"properties": {
"postCode": { "type": "string", "minLength": 4, "maxLength": 5 }
}
}
}
}
Query usage:
MongoJsonSchema schema = MongoJsonSchema.builder()
.required("address")
.property(object("address").properties(string("street").matching("^Apple.*"))).build();
List<Person> person = template.find(query(matchingDocumentStructure(schema)), Person.class));
Collection validation:
MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname")
.properties(string("firstname").possibleValues("luke", "han"),
object("address").properties(string("postCode").minLength(4).maxLength(5)))
.build();
template.createCollection(Person.class, CollectionOptions.empty()
.schema(schema)
.failOnValidationError());
Original pull request: #524.
This commit is contained in:
committed by
Mark Paluch
parent
ddc6e4a219
commit
14ccb5152a
@@ -18,9 +18,14 @@ package org.springframework.data.mongodb.core;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
|
||||
import org.springframework.data.util.Optionals;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.client.model.ValidationAction;
|
||||
import com.mongodb.client.model.ValidationLevel;
|
||||
|
||||
/**
|
||||
* Provides a simple wrapper to encapsulate the variety of settings you can use when creating a collection.
|
||||
*
|
||||
@@ -34,6 +39,7 @@ public class CollectionOptions {
|
||||
private @Nullable Long size;
|
||||
private @Nullable Boolean capped;
|
||||
private @Nullable Collation collation;
|
||||
private Validator validator;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>CollectionOptions</code> instance.
|
||||
@@ -46,16 +52,17 @@ public class CollectionOptions {
|
||||
*/
|
||||
@Deprecated
|
||||
public CollectionOptions(@Nullable Long size, @Nullable Long maxDocuments, @Nullable Boolean capped) {
|
||||
this(size, maxDocuments, capped, null);
|
||||
this(size, maxDocuments, capped, null, Validator.none());
|
||||
}
|
||||
|
||||
private CollectionOptions(@Nullable Long size, @Nullable Long maxDocuments, @Nullable Boolean capped,
|
||||
@Nullable Collation collation) {
|
||||
@Nullable Collation collation, Validator validator) {
|
||||
|
||||
this.maxDocuments = maxDocuments;
|
||||
this.size = size;
|
||||
this.capped = capped;
|
||||
this.collation = collation;
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +76,7 @@ public class CollectionOptions {
|
||||
|
||||
Assert.notNull(collation, "Collation must not be null!");
|
||||
|
||||
return new CollectionOptions(null, null, null, collation);
|
||||
return new CollectionOptions(null, null, null, collation, Validator.none());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +86,7 @@ public class CollectionOptions {
|
||||
* @since 2.0
|
||||
*/
|
||||
public static CollectionOptions empty() {
|
||||
return new CollectionOptions(null, null, null, null);
|
||||
return new CollectionOptions(null, null, null, null, Validator.none());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +97,7 @@ public class CollectionOptions {
|
||||
* @since 2.0
|
||||
*/
|
||||
public CollectionOptions capped() {
|
||||
return new CollectionOptions(size, maxDocuments, true, collation);
|
||||
return new CollectionOptions(size, maxDocuments, true, collation, validator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +108,7 @@ public class CollectionOptions {
|
||||
* @since 2.0
|
||||
*/
|
||||
public CollectionOptions maxDocuments(long maxDocuments) {
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation);
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation, validator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +119,7 @@ public class CollectionOptions {
|
||||
* @since 2.0
|
||||
*/
|
||||
public CollectionOptions size(long size) {
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation);
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation, validator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,7 +130,111 @@ public class CollectionOptions {
|
||||
* @since 2.0
|
||||
*/
|
||||
public CollectionOptions collation(@Nullable Collation collation) {
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation);
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation, validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validator} set to given
|
||||
* {@link MongoJsonSchema}.
|
||||
*
|
||||
* @param schema can be {@literal null}.
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions schema(@Nullable MongoJsonSchema schema) {
|
||||
return validation(new Validator(schema, validator.validationLevel, validator.validationAction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to {@code off}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions disableValidation() {
|
||||
return schemaValidationLevel(ValidationLevel.OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to {@code strict}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions strictValidation() {
|
||||
return schemaValidationLevel(ValidationLevel.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to
|
||||
* {@code moderate}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions moderateValidation() {
|
||||
return schemaValidationLevel(ValidationLevel.MODERATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to {@code warn}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions warnOnValidationError() {
|
||||
return schemaValidationAction(ValidationAction.WARN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to {@code error}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions failOnValidationError() {
|
||||
return schemaValidationAction(ValidationAction.ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set given
|
||||
* {@link ValidationLevel}.
|
||||
*
|
||||
* @param validationLevel must not be {@literal null}.
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions schemaValidationLevel(ValidationLevel validationLevel) {
|
||||
|
||||
Assert.notNull(validationLevel, "ValidationLevel must not be null!");
|
||||
return validation(new Validator(validator.schema, validationLevel, validator.validationAction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set given
|
||||
* {@link ValidationAction}.
|
||||
*
|
||||
* @param validationAction must not be {@literal null}.
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions schemaValidationAction(ValidationAction validationAction) {
|
||||
|
||||
Assert.notNull(validationAction, "ValidationAction must not be null!");
|
||||
return validation(new Validator(validator.schema, validator.validationLevel, validationAction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with the given {@link Validator}.
|
||||
*
|
||||
* @param validator must not be {@literal null}. Use {@link Validator#none()} to remove validation.
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CollectionOptions validation(Validator validator) {
|
||||
|
||||
Assert.notNull(validator, "Validator must not be null!");
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation, validator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,4 +274,83 @@ public class CollectionOptions {
|
||||
public Optional<Collation> getCollation() {
|
||||
return Optional.ofNullable(collation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MongoJsonSchema} for the collection.
|
||||
*
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
* @since 2.1
|
||||
*/
|
||||
public Optional<Validator> getValidator() {
|
||||
return validator.isEmpty() ? Optional.empty() : Optional.of(validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulation of Validator options.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class Validator {
|
||||
|
||||
private static final Validator NONE = new Validator(null, null, null);
|
||||
|
||||
private @Nullable MongoJsonSchema schema;
|
||||
private @Nullable ValidationLevel validationLevel;
|
||||
private @Nullable ValidationAction validationAction;
|
||||
|
||||
private Validator(@Nullable MongoJsonSchema schema, @Nullable ValidationLevel validationLevel,
|
||||
@Nullable ValidationAction validationAction) {
|
||||
|
||||
this.schema = schema;
|
||||
this.validationLevel = validationLevel;
|
||||
this.validationAction = validationAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty {@link Validator}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static Validator none() {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@code $jsonSchema} used for validation.
|
||||
*
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
*/
|
||||
@Nullable
|
||||
public Optional<MongoJsonSchema> getSchema() {
|
||||
return Optional.ofNullable(schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@code validationLevel} to apply.
|
||||
*
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
*/
|
||||
@Nullable
|
||||
public Optional<ValidationLevel> getValidationLevel() {
|
||||
return Optional.ofNullable(validationLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@code validationAction} to perform.
|
||||
*
|
||||
* @return @return {@link Optional#empty()} if not set.
|
||||
*/
|
||||
@Nullable
|
||||
public Optional<ValidationAction> getValidationAction() {
|
||||
return Optional.ofNullable(validationAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if no arguments set.
|
||||
*/
|
||||
boolean isEmpty() {
|
||||
return !Optionals.isAnyPresent(getSchema(), getValidationAction(), getValidationLevel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
|
||||
import org.springframework.data.mongodb.core.CollectionOptions.Validator;
|
||||
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
|
||||
@@ -73,9 +74,11 @@ import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOpe
|
||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.JsonSchemaMapper;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper;
|
||||
import org.springframework.data.mongodb.core.convert.MongoWriter;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.convert.UpdateMapper;
|
||||
@@ -133,14 +136,7 @@ import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoCursor;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.MongoIterable;
|
||||
import com.mongodb.client.model.CountOptions;
|
||||
import com.mongodb.client.model.CreateCollectionOptions;
|
||||
import com.mongodb.client.model.DeleteOptions;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.FindOneAndDeleteOptions;
|
||||
import com.mongodb.client.model.FindOneAndUpdateOptions;
|
||||
import com.mongodb.client.model.ReturnDocument;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import com.mongodb.client.model.*;
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import com.mongodb.util.JSONParseException;
|
||||
@@ -191,6 +187,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
private final QueryMapper queryMapper;
|
||||
private final UpdateMapper updateMapper;
|
||||
private final JsonSchemaMapper schemaMapper;
|
||||
private final SpelAwareProxyProjectionFactory projectionFactory;
|
||||
|
||||
private @Nullable WriteConcern writeConcern;
|
||||
@@ -235,6 +232,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
|
||||
this.queryMapper = new QueryMapper(this.mongoConverter);
|
||||
this.updateMapper = new UpdateMapper(this.mongoConverter);
|
||||
this.schemaMapper = new MongoJsonSchemaMapper(this.mongoConverter);
|
||||
this.projectionFactory = new SpelAwareProxyProjectionFactory();
|
||||
|
||||
// We always have a mapping context in the converter, whether it's a simple one or not
|
||||
@@ -545,7 +543,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
public <T> MongoCollection<Document> createCollection(Class<T> entityClass,
|
||||
@Nullable CollectionOptions collectionOptions) {
|
||||
return createCollection(determineCollectionName(entityClass), collectionOptions);
|
||||
|
||||
Assert.notNull(entityClass, "EntityClass must not be null!");
|
||||
return doCreateCollection(determineCollectionName(entityClass), convertToDocument(collectionOptions, entityClass));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2227,6 +2227,21 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
co.collation(IndexConverters.fromDocument(collectionOptions.get("collation", Document.class)));
|
||||
}
|
||||
|
||||
if (collectionOptions.containsKey("validator")) {
|
||||
|
||||
ValidationOptions options = new ValidationOptions();
|
||||
|
||||
if (collectionOptions.containsKey("validationLevel")) {
|
||||
options.validationLevel(ValidationLevel.fromString(collectionOptions.getString("validationLevel")));
|
||||
}
|
||||
if (collectionOptions.containsKey("validationAction")) {
|
||||
options.validationAction(ValidationAction.fromString(collectionOptions.getString("validationAction")));
|
||||
}
|
||||
|
||||
options.validator(collectionOptions.get("validator", Document.class));
|
||||
co.validationOptions(options);
|
||||
}
|
||||
|
||||
db.createCollection(collectionName, co);
|
||||
|
||||
MongoCollection<Document> coll = db.getCollection(collectionName, Document.class);
|
||||
@@ -2339,6 +2354,35 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
new ProjectingReadCallback<>(mongoConverter, sourceClass, targetClass, collectionName), collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert given {@link CollectionOptions} to a document and take the domain type information into account when
|
||||
* creating a mapped schema for validation. <br />
|
||||
* This method calls {@link #convertToDocument(CollectionOptions)} for backwards compatibility and potentially
|
||||
* overwrites the validator with the mapped validator document. In the long run
|
||||
* {@link #convertToDocument(CollectionOptions)} will be removed so that this one becomes the only source of truth.
|
||||
*
|
||||
* @param collectionOptions can be {@literal null}.
|
||||
* @param targetType must not be {@literal null}. Use {@link Object} type instead.
|
||||
* @return never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
protected Document convertToDocument(@Nullable CollectionOptions collectionOptions, Class<?> targetType) {
|
||||
|
||||
Document doc = convertToDocument(collectionOptions);
|
||||
|
||||
if (collectionOptions.getValidator().isPresent()) {
|
||||
Validator v = collectionOptions.getValidator().get();
|
||||
v.getSchema().ifPresent(val -> doc.put("validator", schemaMapper.mapSchema(val.toDocument(), targetType)));
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param collectionOptions can be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @deprecated since 2.1 in favor of {@link #convertToDocument(CollectionOptions, Class)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected Document convertToDocument(@Nullable CollectionOptions collectionOptions) {
|
||||
|
||||
Document document = new Document();
|
||||
@@ -2348,6 +2392,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
collectionOptions.getSize().ifPresent(val -> document.put("size", val));
|
||||
collectionOptions.getMaxDocuments().ifPresent(val -> document.put("max", val));
|
||||
collectionOptions.getCollation().ifPresent(val -> document.append("collation", val.toDocument()));
|
||||
|
||||
if (collectionOptions.getValidator().isPresent()) {
|
||||
Validator v = collectionOptions.getValidator().get();
|
||||
v.getValidationLevel().ifPresent(val -> document.append("validationLevel", val));
|
||||
v.getValidationAction().ifPresent(val -> document.append("validationAction", val));
|
||||
v.getSchema().ifPresent(val -> document.append("validator",
|
||||
new MongoJsonSchemaMapper(getConverter()).mapSchema(val.toDocument(), Object.class)));
|
||||
}
|
||||
}
|
||||
return document;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.convert;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
* {@link JsonSchemaMapper} allows mapping a given {@link Document} containing a {@literal $jsonSchema} to the fields of
|
||||
* a given domain type. The mapping considers {@link org.springframework.data.mongodb.core.mapping.Field} annotations
|
||||
* and other Spring Data specifics.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface JsonSchemaMapper {
|
||||
|
||||
/**
|
||||
* Map the {@literal required} and {@literal properties} fields the given {@link Document} containing the
|
||||
* {@literal $jsonSchema} against the given domain type. <br />
|
||||
* The source document remains untouched, fields that do not require mapping are simply copied over to the mapped
|
||||
* instance.
|
||||
*
|
||||
* @param jsonSchema the {@link Document} holding the raw schema representation. Must not be {@literal null}.
|
||||
* @param type the target type to map against. Must not be {@literal null}.
|
||||
* @return a <strong>new</strong> {@link Document} containing the mapped {@literal $jsonSchema} never {@literal null}.
|
||||
*/
|
||||
Document mapSchema(Document jsonSchema, Class<?> type);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link JsonSchemaMapper} implementation using the conversion and mapping infrastructure for mapping fields to the
|
||||
* provided domain type.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public class MongoJsonSchemaMapper implements JsonSchemaMapper {
|
||||
|
||||
private static final String $JSON_SCHEMA = "$jsonSchema";
|
||||
private static final String REQUIRED_FIELD = "required";
|
||||
private static final String PROPERTIES_FIELD = "properties";
|
||||
private static final String ENUM_FIELD = "enum";
|
||||
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
private final MongoConverter converter;
|
||||
|
||||
/**
|
||||
* Create a new {@link MongoJsonSchemaMapper} facilitating the given {@link MongoConverter}.
|
||||
*
|
||||
* @param converter must not be {@literal null}.
|
||||
*/
|
||||
public MongoJsonSchemaMapper(MongoConverter converter) {
|
||||
|
||||
Assert.notNull(converter, "Converter must not be null!");
|
||||
|
||||
this.converter = converter;
|
||||
this.mappingContext = converter.getMappingContext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.JsonSchemaMapper#mapSchema(org.springframework.data.mongodb.core.schema.MongoJsonSchema, java.lang.Class)
|
||||
*/
|
||||
public Document mapSchema(Document jsonSchema, Class<?> type) {
|
||||
|
||||
Assert.notNull(jsonSchema, "Schema must not be null!");
|
||||
Assert.notNull(type, "Type must not be null! Please consider Object.class.");
|
||||
Assert.isTrue(jsonSchema.containsKey($JSON_SCHEMA),
|
||||
() -> String.format("Document does not contain $jsonSchema field. Found %s.", jsonSchema));
|
||||
|
||||
if (Object.class.equals(type)) {
|
||||
return new Document(jsonSchema);
|
||||
}
|
||||
|
||||
return new Document($JSON_SCHEMA,
|
||||
mapSchemaObject(mappingContext.getPersistentEntity(type), jsonSchema.get($JSON_SCHEMA, Document.class)));
|
||||
}
|
||||
|
||||
private Document mapSchemaObject(@Nullable PersistentEntity entity, Document source) {
|
||||
|
||||
Document sink = new Document(source);
|
||||
|
||||
if (source.containsKey(REQUIRED_FIELD)) {
|
||||
sink.replace(REQUIRED_FIELD, mapRequiredProperties(entity, source.get(REQUIRED_FIELD, Collection.class)));
|
||||
}
|
||||
|
||||
if (source.containsKey(PROPERTIES_FIELD)) {
|
||||
sink.replace(PROPERTIES_FIELD, mapProperties(entity, source.get(PROPERTIES_FIELD, Document.class)));
|
||||
}
|
||||
|
||||
mapEnumValuesIfNecessary(sink);
|
||||
|
||||
return sink;
|
||||
}
|
||||
|
||||
private Document mapProperties(@Nullable PersistentEntity<?, MongoPersistentProperty> entity, Document source) {
|
||||
|
||||
Document sink = new Document();
|
||||
for (String fieldName : source.keySet()) {
|
||||
|
||||
String mappedFieldName = getFieldName(entity, fieldName);
|
||||
Document mappedProperty = mapProperty(entity, fieldName, source.get(fieldName, Document.class));
|
||||
|
||||
sink.append(mappedFieldName, mappedProperty);
|
||||
}
|
||||
return sink;
|
||||
}
|
||||
|
||||
private List<String> mapRequiredProperties(@Nullable PersistentEntity<?, MongoPersistentProperty> entity,
|
||||
Collection<String> sourceFields) {
|
||||
|
||||
return sourceFields.stream() ///
|
||||
.map(fieldName -> getFieldName(entity, fieldName)) //
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Document mapProperty(@Nullable PersistentEntity<?, MongoPersistentProperty> entity, String sourceFieldName,
|
||||
Document source) {
|
||||
|
||||
Document sink = new Document(source);
|
||||
|
||||
if (entity != null && sink.containsKey(Type.objectType().representation())) {
|
||||
|
||||
MongoPersistentProperty property = entity.getPersistentProperty(sourceFieldName);
|
||||
if (property != null && property.isEntity()) {
|
||||
sink = mapSchemaObject(mappingContext.getPersistentEntity(property.getActualType()), source);
|
||||
}
|
||||
}
|
||||
|
||||
return mapEnumValuesIfNecessary(sink);
|
||||
}
|
||||
|
||||
private Document mapEnumValuesIfNecessary(Document source) {
|
||||
|
||||
Document sink = new Document(source);
|
||||
if (source.containsKey(ENUM_FIELD)) {
|
||||
sink.replace(ENUM_FIELD, mapEnumValues(source.get(ENUM_FIELD, Iterable.class)));
|
||||
}
|
||||
return sink;
|
||||
}
|
||||
|
||||
private List<Object> mapEnumValues(Iterable<?> values) {
|
||||
|
||||
List<Object> converted = new ArrayList<>();
|
||||
for (Object val : values) {
|
||||
converted.add(converter.convertToMongoType(val));
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
private String getFieldName(@Nullable PersistentEntity<?, MongoPersistentProperty> entity, String sourceField) {
|
||||
|
||||
if (entity == null) {
|
||||
return sourceField;
|
||||
}
|
||||
|
||||
MongoPersistentProperty property = entity.getPersistentProperty(sourceField);
|
||||
return property != null ? property.getFieldName() : sourceField;
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,7 @@ public class QueryMapper {
|
||||
private final MongoConverter converter;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
private final MongoExampleMapper exampleMapper;
|
||||
private final MongoJsonSchemaMapper schemaMapper;
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryMapper} with the given {@link MongoConverter}.
|
||||
@@ -94,6 +95,7 @@ public class QueryMapper {
|
||||
this.converter = converter;
|
||||
this.mappingContext = converter.getMappingContext();
|
||||
this.exampleMapper = new MongoExampleMapper(converter);
|
||||
this.schemaMapper = new MongoJsonSchemaMapper(converter);
|
||||
}
|
||||
|
||||
public Document getMappedObject(Bson query, Optional<? extends MongoPersistentEntity<?>> entity) {
|
||||
@@ -272,6 +274,10 @@ public class QueryMapper {
|
||||
return exampleMapper.getMappedExample(keyword.<Example<?>> getValue(), entity);
|
||||
}
|
||||
|
||||
if (keyword.isJsonSchema()) {
|
||||
return schemaMapper.mapSchema(new Document(keyword.getKey(), keyword.getValue()), entity.getType());
|
||||
}
|
||||
|
||||
return new Document(keyword.getKey(), convertSimpleOrDocument(keyword.getValue(), entity));
|
||||
}
|
||||
|
||||
@@ -599,6 +605,7 @@ public class QueryMapper {
|
||||
* Value object to capture a query keyword representation.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
static class Keyword {
|
||||
|
||||
@@ -666,6 +673,16 @@ public class QueryMapper {
|
||||
public <T> T getValue() {
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current keyword indicates a {@literal $jsonSchema} object.
|
||||
*
|
||||
* @return {@literal true} if {@code key} equals {@literal $jsonSchema}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public boolean isJsonSchema() {
|
||||
return "$jsonSchema".equalsIgnoreCase(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bson.BSON;
|
||||
import org.bson.BsonRegularExpression;
|
||||
@@ -35,6 +36,9 @@ import org.springframework.data.geo.Shape;
|
||||
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJson;
|
||||
import org.springframework.data.mongodb.core.geo.Sphere;
|
||||
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
|
||||
import org.springframework.data.mongodb.core.schema.JsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -115,6 +119,20 @@ public class Criteria implements CriteriaDefinition {
|
||||
return new Criteria().alike(example);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create a {@link Criteria} matching documents against a given structure defined by the
|
||||
* {@link MongoJsonSchema} using ({@code $jsonSchema}) operator.
|
||||
*
|
||||
* @param schema must not be {@literal null}.
|
||||
* @return this
|
||||
* @since 2.1
|
||||
* @see <a href="https://docs.mongodb.com/manual/reference/operator/query/jsonSchema/">MongoDB Query operator:
|
||||
* $jsonSchema</a>
|
||||
*/
|
||||
public static Criteria matchingDocumentStructure(MongoJsonSchema schema) {
|
||||
return new Criteria().andDocumentStructureMatches(schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create a Criteria using the provided key
|
||||
*
|
||||
@@ -335,6 +353,23 @@ public class Criteria implements CriteriaDefinition {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the {@literal $type} operator.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return this
|
||||
* @since 2.1
|
||||
* @see <a href="https://docs.mongodb.com/manual/reference/operator/query/type/">MongoDB Query operator: $type</a>
|
||||
*/
|
||||
public Criteria type(Type... types) {
|
||||
|
||||
Assert.notNull(types, "Types must not be null!");
|
||||
Assert.noNullElements(types, "Types must not contain null.");
|
||||
|
||||
criteria.put("$type", Arrays.asList(types).stream().map(Type::value).collect(Collectors.toList()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the {@literal $not} meta operator which affects the clause directly following
|
||||
*
|
||||
@@ -563,6 +598,30 @@ public class Criteria implements CriteriaDefinition {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion ({@code $jsonSchema}) matching documents against a given structure defined by the
|
||||
* {@link MongoJsonSchema}. <br />
|
||||
* <strong>NOTE:</strong> {@code $jsonSchema} cannot be used on field/property level but defines the whole document
|
||||
* structure. Please use
|
||||
* {@link org.springframework.data.mongodb.core.schema.MongoJsonSchema.MongoJsonSchemaBuilder#properties(JsonSchemaProperty...)}
|
||||
* to specify nested fields or query them using the {@link #type(Type...) $type} operator.
|
||||
*
|
||||
* @param schema must not be {@literal null}.
|
||||
* @return this
|
||||
* @since 2.1
|
||||
* @see <a href="https://docs.mongodb.com/manual/reference/operator/query/jsonSchema/">MongoDB Query operator:
|
||||
* $jsonSchema</a>
|
||||
*/
|
||||
public Criteria andDocumentStructureMatches(MongoJsonSchema schema) {
|
||||
|
||||
Assert.notNull(schema, "Schema must not be null!");
|
||||
|
||||
Criteria schemaCriteria = new Criteria();
|
||||
schemaCriteria.criteria.putAll(schema.toDocument());
|
||||
|
||||
return registerCriteriaChainElement(schemaCriteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an 'or' criteria using the $or operator for all of the provided criteria
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.schema;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.BooleanJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.NullJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.NumericJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.StringJsonSchemaObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link JsonSchemaProperty} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implements JsonSchemaProperty {
|
||||
|
||||
protected String identifier;
|
||||
protected T jsonSchemaObjectDelegate;
|
||||
|
||||
public IdentifiableJsonSchemaProperty(String identifier, T jsonSchemaObject) {
|
||||
|
||||
Assert.notNull(identifier, "Identifier must not be null!");
|
||||
Assert.notNull(jsonSchemaObject, "JsonSchemaObject must not be null!");
|
||||
|
||||
this.identifier = identifier;
|
||||
this.jsonSchemaObjectDelegate = jsonSchemaObject;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaProperty#getIdentifier()
|
||||
*/
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject#toDocument()
|
||||
*/
|
||||
@Override
|
||||
public Document toDocument() {
|
||||
return new Document(identifier, jsonSchemaObjectDelegate.toDocument());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject#getTypes()
|
||||
*/
|
||||
@Override
|
||||
public Set<Type> getTypes() {
|
||||
return jsonSchemaObjectDelegate.getTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience {@link JsonSchemaProperty} implementation without a {@code type} property.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class UntypedJsonSchemaProperty extends IdentifiableJsonSchemaProperty<UntypedJsonSchemaObject> {
|
||||
|
||||
public UntypedJsonSchemaProperty(String identifier, UntypedJsonSchemaObject jsonSchemaObject) {
|
||||
super(identifier, jsonSchemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty possibleValues(Object... possibleValues) {
|
||||
return possibleValues(Arrays.asList(possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
|
||||
return allOf(new LinkedHashSet<>(Arrays.asList(allOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
|
||||
return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
|
||||
return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty possibleValues(Collection<Object> possibleValues) {
|
||||
return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
|
||||
return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
|
||||
return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
|
||||
return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notMatch must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#notMatch(JsonSchemaObject)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
|
||||
return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#description(String)
|
||||
*/
|
||||
public UntypedJsonSchemaProperty description(String description) {
|
||||
return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
public UntypedJsonSchemaProperty generatedDescription() {
|
||||
return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience {@link JsonSchemaProperty} implementation for a {@code type : 'string'} property.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class StringJsonSchemaProperty extends IdentifiableJsonSchemaProperty<StringJsonSchemaObject> {
|
||||
|
||||
/**
|
||||
* @param identifier identifier the {@literal property} name or {@literal patternProperty} regex. Must not be
|
||||
* {@literal null} nor {@literal empty}.
|
||||
* @param schemaObject must not be {@literal null}.
|
||||
*/
|
||||
public StringJsonSchemaProperty(String identifier, StringJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param length
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#minLength(int)
|
||||
*/
|
||||
public StringJsonSchemaProperty minLength(int length) {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minLength(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param length
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#maxLength(int)
|
||||
*/
|
||||
public StringJsonSchemaProperty maxLength(int length) {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxLength(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pattern must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#matching(String)
|
||||
*/
|
||||
public StringJsonSchemaProperty matching(String pattern) {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.matching(pattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty possibleValues(String... possibleValues) {
|
||||
return possibleValues(Arrays.asList(possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
|
||||
return allOf(new LinkedHashSet<>(Arrays.asList(allOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
|
||||
return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
|
||||
return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty possibleValues(Collection<String> possibleValues) {
|
||||
return new StringJsonSchemaProperty(identifier,
|
||||
jsonSchemaObjectDelegate.possibleValues((Collection) possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notMatch must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#notMatch(JsonSchemaObject)
|
||||
*/
|
||||
public StringJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#description(String)
|
||||
*/
|
||||
public StringJsonSchemaProperty description(String description) {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
* @see StringJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
public StringJsonSchemaProperty generatedDescription() {
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience {@link JsonSchemaProperty} implementation for a {@code type : 'object'} property.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class ObjectJsonSchemaProperty extends IdentifiableJsonSchemaProperty<ObjectJsonSchemaObject> {
|
||||
|
||||
/**
|
||||
* @param identifier identifier the {@literal property} name or {@literal patternProperty} regex. Must not be
|
||||
* {@literal null} nor {@literal empty}.
|
||||
* @param schemaObject must not be {@literal null}.
|
||||
*/
|
||||
public ObjectJsonSchemaProperty(String identifier, ObjectJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param range must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#nrProperties
|
||||
*/
|
||||
public ObjectJsonSchemaProperty nrProperties(Range<Integer> range) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.nrProperties(range));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nrProperties must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#minNrProperties(int)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty minNrProperties(int nrProperties) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minNrProperties(nrProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nrProperties must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#maxNrProperties(int)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty maxNrProperties(int nrProperties) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxNrProperties(nrProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#required(String...)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty required(String... properties) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.required(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param additionalPropertiesAllowed
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#additionalProperties(boolean)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty additionalProperties(boolean additionalPropertiesAllowed) {
|
||||
return new ObjectJsonSchemaProperty(identifier,
|
||||
jsonSchemaObjectDelegate.additionalProperties(additionalPropertiesAllowed));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param additionalProperties must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#additionalProperties(ObjectJsonSchemaObject)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty additionalProperties(ObjectJsonSchemaObject additionalProperties) {
|
||||
return new ObjectJsonSchemaProperty(identifier,
|
||||
jsonSchemaObjectDelegate.additionalProperties(additionalProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#properties(JsonSchemaProperty...)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty properties(JsonSchemaProperty... properties) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.properties(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty possibleValues(Object... possibleValues) {
|
||||
return possibleValues(Arrays.asList(possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
|
||||
return allOf(new LinkedHashSet<>(Arrays.asList(allOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
|
||||
return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
|
||||
return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty possibleValues(Collection<Object> possibleValues) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notMatch must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#notMatch(JsonSchemaObject)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#description(String)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty description(String description) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
public ObjectJsonSchemaProperty generatedDescription() {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience {@link JsonSchemaProperty} implementation for a {@code type : 'number'} property.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class NumericJsonSchemaProperty extends IdentifiableJsonSchemaProperty<NumericJsonSchemaObject> {
|
||||
|
||||
/**
|
||||
* @param identifier identifier the {@literal property} name or {@literal patternProperty} regex. Must not be
|
||||
* {@literal null} nor {@literal empty}.
|
||||
* @param schemaObject must not be {@literal null}.
|
||||
*/
|
||||
public NumericJsonSchemaProperty(String identifier, NumericJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#multipleOf
|
||||
*/
|
||||
public NumericJsonSchemaProperty multipleOf(Number value) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.multipleOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param range must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#within(Range)
|
||||
*/
|
||||
public NumericJsonSchemaProperty within(Range<? extends Number> range) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.within(range));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param min must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#gt(Number)
|
||||
*/
|
||||
public NumericJsonSchemaProperty gt(Number min) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.gt(min));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param min must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#gte(Number)
|
||||
*/
|
||||
public NumericJsonSchemaProperty gte(Number min) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.gte(min));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param max must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#lt(Number)
|
||||
*/
|
||||
public NumericJsonSchemaProperty lt(Number max) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.lt(max));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param max must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#lte(Number)
|
||||
*/
|
||||
public NumericJsonSchemaProperty lte(Number max) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.lte(max));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty possibleValues(Number... possibleValues) {
|
||||
return possibleValues(new LinkedHashSet<>(Arrays.asList(possibleValues)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
|
||||
return allOf(Arrays.asList(allOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
|
||||
return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
|
||||
return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty possibleValues(Collection<Number> possibleValues) {
|
||||
return new NumericJsonSchemaProperty(identifier,
|
||||
jsonSchemaObjectDelegate.possibleValues((Collection) possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notMatch must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#notMatch(JsonSchemaObject)
|
||||
*/
|
||||
public NumericJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#description(String)
|
||||
*/
|
||||
public NumericJsonSchemaProperty description(String description) {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NumericJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
public NumericJsonSchemaProperty generatedDescription() {
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience {@link JsonSchemaProperty} implementation for a {@code type : 'array'} property.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class ArrayJsonSchemaProperty extends IdentifiableJsonSchemaProperty<ArrayJsonSchemaObject> {
|
||||
|
||||
/**
|
||||
* @param identifier identifier the {@literal property} name or {@literal patternProperty} regex. Must not be
|
||||
* {@literal null} nor {@literal empty}.
|
||||
* @param schemaObject must not be {@literal null}.
|
||||
*/
|
||||
public ArrayJsonSchemaProperty(String identifier, ArrayJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uniqueItems
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#uniqueItems(boolean)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty uniqueItems(boolean uniqueItems) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.uniqueItems(uniqueItems));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param range must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#range(Range)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty range(Range<Integer> range) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.range(range));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param items must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#items(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty items(Collection<JsonSchemaObject> items) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.items(items));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty possibleValues(Object... possibleValues) {
|
||||
return possibleValues(new LinkedHashSet<>(Arrays.asList(possibleValues)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
|
||||
return allOf(new LinkedHashSet<>(Arrays.asList(allOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
|
||||
return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
|
||||
return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty possibleValues(Collection<Object> possibleValues) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notMatch must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#notMatch(JsonSchemaObject)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObjecty#description(String)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty description(String description) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
public ArrayJsonSchemaProperty generatedDescription() {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience {@link JsonSchemaProperty} implementation for a {@code type : 'boolean'} property.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class BooleanJsonSchemaProperty extends IdentifiableJsonSchemaProperty<BooleanJsonSchemaObject> {
|
||||
|
||||
public BooleanJsonSchemaProperty(String identifier, BooleanJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see BooleanJsonSchemaObject#description(String)
|
||||
*/
|
||||
public BooleanJsonSchemaProperty description(String description) {
|
||||
return new BooleanJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new instance of {@link BooleanJsonSchemaProperty}.
|
||||
* @see BooleanJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
public BooleanJsonSchemaProperty generatedDescription() {
|
||||
return new BooleanJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience {@link JsonSchemaProperty} implementation for a {@code type : 'null'} property.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class NullJsonSchemaProperty extends IdentifiableJsonSchemaProperty<NullJsonSchemaObject> {
|
||||
|
||||
public NullJsonSchemaProperty(String identifier, NullJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see NullJsonSchemaObject#description(String)
|
||||
*/
|
||||
public NullJsonSchemaProperty description(String description) {
|
||||
return new NullJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.schema;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.BooleanJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.NullJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.NumericJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.StringJsonSchemaObject;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface JsonSchemaObject {
|
||||
|
||||
/**
|
||||
* Get the set of types defined for this schema element.<br />
|
||||
* The {@link Set} is likely to contain only one element in most cases.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Set<Type> getTypes();
|
||||
|
||||
/**
|
||||
* Get the MongoDB specific representation.<br />
|
||||
* The Document may contain fields (eg. like {@literal bsonType}) not contained in the JsonSchema specification. It
|
||||
* may also contain types not directly processable by the MongoDB java driver. Make sure to run the produced
|
||||
* {@link Document} through the mapping infrastructure.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Document toDocument();
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} of {@code type : 'object'}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static ObjectJsonSchemaObject object() {
|
||||
return new ObjectJsonSchemaObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} of {@code type : 'string'}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static StringJsonSchemaObject string() {
|
||||
return new StringJsonSchemaObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} of {@code type : 'number'}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static NumericJsonSchemaObject number() {
|
||||
return new NumericJsonSchemaObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} of {@code type : 'array'}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static ArrayJsonSchemaObject array() {
|
||||
return new ArrayJsonSchemaObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} of {@code type : 'boolean'}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static BooleanJsonSchemaObject bool() {
|
||||
return new BooleanJsonSchemaObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} of {@code type : 'null'}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static NullJsonSchemaObject nil() {
|
||||
return new NullJsonSchemaObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} of given {@link Type}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static TypedJsonSchemaObject of(Type type) {
|
||||
return TypedJsonSchemaObject.of(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link UntypedJsonSchemaObject}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static UntypedJsonSchemaObject untyped() {
|
||||
return new UntypedJsonSchemaObject(null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} matching the given {@code type}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
static TypedJsonSchemaObject of(@Nullable Class<?> type) {
|
||||
|
||||
if (type == null) {
|
||||
return of(Type.nullType());
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
|
||||
if (type.equals(byte[].class)) {
|
||||
return of(Type.binaryType());
|
||||
}
|
||||
|
||||
return of(Type.arrayType());
|
||||
}
|
||||
|
||||
if (type.equals(Object.class)) {
|
||||
return of(Type.objectType());
|
||||
}
|
||||
|
||||
if (type.equals(ObjectId.class)) {
|
||||
return of(Type.objectIdType());
|
||||
}
|
||||
|
||||
if (ClassUtils.isAssignable(String.class, type)) {
|
||||
return of(Type.stringType());
|
||||
}
|
||||
|
||||
if (ClassUtils.isAssignable(Date.class, type)) {
|
||||
return of(Type.dateType());
|
||||
}
|
||||
|
||||
if (ClassUtils.isAssignable(Pattern.class, type)) {
|
||||
return of(Type.regexType());
|
||||
}
|
||||
|
||||
if (ClassUtils.isAssignable(Boolean.class, type)) {
|
||||
return of(Type.booleanType());
|
||||
}
|
||||
|
||||
if (ClassUtils.isAssignable(Number.class, type)) {
|
||||
|
||||
if (type.equals(Long.class)) {
|
||||
return of(Type.longType());
|
||||
}
|
||||
|
||||
if (type.equals(Float.class)) {
|
||||
return of(Type.doubleType());
|
||||
}
|
||||
|
||||
if (type.equals(Double.class)) {
|
||||
return of(Type.doubleType());
|
||||
}
|
||||
|
||||
if (type.equals(Integer.class)) {
|
||||
return of(Type.intType());
|
||||
}
|
||||
|
||||
if (type.equals(BigDecimal.class)) {
|
||||
return of(Type.bigDecimalType());
|
||||
}
|
||||
|
||||
return of(Type.numberType());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("No json schema type found for %s.", type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Type represents either a json schema {@literal type} or a MongoDB specific {@literal bsonType}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
interface Type {
|
||||
|
||||
// BSON TYPES
|
||||
final Type OBJECT_ID = bsonTypeOf("objectId");
|
||||
final Type REGULAR_EXPRESSION = bsonTypeOf("regex");
|
||||
final Type DOUBLE = bsonTypeOf("double");
|
||||
final Type BINARY_DATA = bsonTypeOf("binData");
|
||||
final Type DATE = bsonTypeOf("date");
|
||||
final Type JAVA_SCRIPT = bsonTypeOf("javascript");
|
||||
final Type INT_32 = bsonTypeOf("int");
|
||||
final Type INT_64 = bsonTypeOf("long");
|
||||
final Type DECIMAL_128 = bsonTypeOf("decimal");
|
||||
final Type TIMESTAMP = bsonTypeOf("timestamp");
|
||||
|
||||
final Set<Type> BSON_TYPES = new HashSet<>(Arrays.asList(OBJECT_ID, REGULAR_EXPRESSION, DOUBLE, BINARY_DATA, DATE,
|
||||
JAVA_SCRIPT, INT_32, INT_64, DECIMAL_128, TIMESTAMP));
|
||||
|
||||
// JSON SCHEMA TYPES
|
||||
final Type OBJECT = jsonTypeOf("object");
|
||||
final Type ARRAY = jsonTypeOf("array");
|
||||
final Type NUMBER = jsonTypeOf("number");
|
||||
final Type BOOLEAN = jsonTypeOf("boolean");
|
||||
final Type STRING = jsonTypeOf("string");
|
||||
final Type NULL = jsonTypeOf("null");
|
||||
|
||||
final Set<Type> JSON_TYPES = new HashSet<>(Arrays.asList(OBJECT, ARRAY, NUMBER, BOOLEAN, STRING, NULL));
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'objectId' }.
|
||||
*/
|
||||
static Type objectIdType() {
|
||||
return OBJECT_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'regex' }.
|
||||
*/
|
||||
static Type regexType() {
|
||||
return REGULAR_EXPRESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'double' }.
|
||||
*/
|
||||
static Type doubleType() {
|
||||
return DOUBLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'binData' }.
|
||||
*/
|
||||
static Type binaryType() {
|
||||
return BINARY_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'date' }.
|
||||
*/
|
||||
static Type dateType() {
|
||||
return DATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'javascript' }.
|
||||
*/
|
||||
static Type javascriptType() {
|
||||
return JAVA_SCRIPT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'int' }.
|
||||
*/
|
||||
static Type intType() {
|
||||
return INT_32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'long' }.
|
||||
*/
|
||||
static Type longType() {
|
||||
return INT_64;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'decimal128' }.
|
||||
*/
|
||||
static Type bigDecimalType() {
|
||||
return DECIMAL_128;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'timestamp' }.
|
||||
*/
|
||||
static Type timestampType() {
|
||||
return TIMESTAMP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code type : 'object' }.
|
||||
*/
|
||||
static Type objectType() {
|
||||
return OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code type : 'array' }.
|
||||
*/
|
||||
static Type arrayType() {
|
||||
return ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code type : 'number' }.
|
||||
*/
|
||||
static Type numberType() {
|
||||
return NUMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code type : 'boolean' }.
|
||||
*/
|
||||
static Type booleanType() {
|
||||
return BOOLEAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code type : 'string' }.
|
||||
*/
|
||||
static Type stringType() {
|
||||
return STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code type : 'null' }.
|
||||
*/
|
||||
static Type nullType() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new {@link Type} representing the given {@code bsonType}.
|
||||
*/
|
||||
static Type bsonTypeOf(String name) {
|
||||
return new BsonType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new {@link Type} representing the given {@code type}.
|
||||
*/
|
||||
static Type jsonTypeOf(String name) {
|
||||
return new JsonType(name);
|
||||
}
|
||||
|
||||
static Set<Type> jsonTypes() {
|
||||
return JSON_TYPES;
|
||||
}
|
||||
|
||||
static Set<Type> bsonTypes() {
|
||||
return BSON_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Type} representation. Either {@code type} or {@code bsonType}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
String representation();
|
||||
|
||||
/**
|
||||
* Get the {@link Type} value. Like {@literal string}, {@literal number},...
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Object value();
|
||||
|
||||
/**
|
||||
* @author Christpoh Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class JsonType implements Type {
|
||||
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public String representation() {
|
||||
return "type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String value() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christpoh Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class BsonType implements Type {
|
||||
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public String representation() {
|
||||
return "bsonType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String value() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.schema;
|
||||
|
||||
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.ArrayJsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.BooleanJsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.NullJsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.NumericJsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.ObjectJsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.StringJsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.UntypedJsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.NumericJsonSchemaObject;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
|
||||
|
||||
/**
|
||||
* A {@literal property} or {@literal patternProperty} within a {@link JsonSchemaObject} of {@code type : 'object'}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface JsonSchemaProperty extends JsonSchemaObject {
|
||||
|
||||
/**
|
||||
* The identifier can be either the property name or the regex expression properties have to match when used along
|
||||
* with {@link ObjectJsonSchemaObject#patternProperties(JsonSchemaProperty...)}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
String getIdentifier();
|
||||
|
||||
/**
|
||||
* Creates a new {@link UntypedJsonSchemaProperty} with given {@literal identifier} without {@code type}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link UntypedJsonSchemaProperty}.
|
||||
*/
|
||||
static UntypedJsonSchemaProperty untyped(String identifier) {
|
||||
return new UntypedJsonSchemaProperty(identifier, JsonSchemaObject.untyped());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link StringJsonSchemaProperty} with given {@literal identifier} of {@code type : 'string'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link StringJsonSchemaProperty}.
|
||||
*/
|
||||
static StringJsonSchemaProperty string(String identifier) {
|
||||
return new StringJsonSchemaProperty(identifier, JsonSchemaObject.string());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ObjectJsonSchemaProperty} with given {@literal identifier} of {@code type : 'object'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
*/
|
||||
static ObjectJsonSchemaProperty object(String identifier) {
|
||||
return new ObjectJsonSchemaProperty(identifier, JsonSchemaObject.object());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NumericJsonSchemaProperty} with given {@literal identifier} of {@code type : 'number'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
*/
|
||||
static NumericJsonSchemaProperty number(String identifier) {
|
||||
return new NumericJsonSchemaProperty(identifier, JsonSchemaObject.number());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NumericJsonSchemaProperty} with given {@literal identifier} of {@code bsonType : 'int'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
*/
|
||||
static NumericJsonSchemaProperty int32(String identifier) {
|
||||
return new NumericJsonSchemaProperty(identifier, new NumericJsonSchemaObject(Type.intType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NumericJsonSchemaProperty} with given {@literal identifier} of {@code bsonType : 'long'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
*/
|
||||
static NumericJsonSchemaProperty int64(String identifier) {
|
||||
return new NumericJsonSchemaProperty(identifier, new NumericJsonSchemaObject(Type.longType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NumericJsonSchemaProperty} with given {@literal identifier} of {@code bsonType : 'double'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
*/
|
||||
static NumericJsonSchemaProperty float64(String identifier) {
|
||||
return new NumericJsonSchemaProperty(identifier, new NumericJsonSchemaObject(Type.doubleType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NumericJsonSchemaProperty} with given {@literal identifier} of
|
||||
* {@code bsonType : 'decimal128'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
*/
|
||||
static NumericJsonSchemaProperty decimal128(String identifier) {
|
||||
return new NumericJsonSchemaProperty(identifier, new NumericJsonSchemaObject(Type.bigDecimalType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ArrayJsonSchemaProperty} with given {@literal identifier} of {@code type : 'array'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
*/
|
||||
static ArrayJsonSchemaProperty array(String identifier) {
|
||||
return new ArrayJsonSchemaProperty(identifier, JsonSchemaObject.array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BooleanJsonSchemaProperty} with given {@literal identifier} of {@code type : 'boolean'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
*/
|
||||
static BooleanJsonSchemaProperty bool(String identifier) {
|
||||
return new BooleanJsonSchemaProperty(identifier, JsonSchemaObject.bool());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BooleanJsonSchemaProperty} with given {@literal identifier} of {@code type : 'null'}.
|
||||
*
|
||||
* @param identifier the {@literal property} name or {@literal patternProperty} regex. Must not be {@literal null} nor
|
||||
* {@literal empty}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
*/
|
||||
static NullJsonSchemaProperty nil(String identifier) {
|
||||
return new NullJsonSchemaProperty(identifier, JsonSchemaObject.nil());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a builder to create a {@link JsonSchemaProperty}.
|
||||
*
|
||||
* @param identifier
|
||||
* @return
|
||||
*/
|
||||
static JsonSchemaPropertyBuilder named(String identifier) {
|
||||
return new JsonSchemaPropertyBuilder(identifier);
|
||||
}
|
||||
|
||||
class JsonSchemaPropertyBuilder {
|
||||
|
||||
private String identifier;
|
||||
|
||||
public JsonSchemaPropertyBuilder(String identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public IdentifiableJsonSchemaProperty<TypedJsonSchemaObject> ofType(Type type) {
|
||||
return new IdentifiableJsonSchemaProperty(identifier, TypedJsonSchemaObject.of(type));
|
||||
}
|
||||
|
||||
public IdentifiableJsonSchemaProperty<TypedJsonSchemaObject> with(TypedJsonSchemaObject schemaObject) {
|
||||
return new IdentifiableJsonSchemaProperty(identifier, schemaObject);
|
||||
}
|
||||
|
||||
public IdentifiableJsonSchemaProperty<UntypedJsonSchemaObject> withoutType() {
|
||||
return new IdentifiableJsonSchemaProperty(identifier, UntypedJsonSchemaObject.newInstance());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.schema;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
**/
|
||||
public class MongoJsonSchema {
|
||||
|
||||
private final JsonSchemaObject root;
|
||||
|
||||
private MongoJsonSchema(JsonSchemaObject root) {
|
||||
|
||||
Assert.notNull(root, "Root must not be null!");
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link Document} containing the specified {@code $jsonSchema}. <br />
|
||||
* Property and field names still need to be mapped to the domain type ones by running the {@link Document} through a
|
||||
* {@link org.springframework.data.mongodb.core.convert.JsonSchemaMapper}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public Document toDocument() {
|
||||
return new Document("$jsonSchema", root.toDocument());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MongoJsonSchema} for a given root object.
|
||||
*
|
||||
* @param root must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static MongoJsonSchema of(JsonSchemaObject root) {
|
||||
return new MongoJsonSchema(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a new {@link MongoJsonSchemaBuilder} to fluently define the schema.
|
||||
*
|
||||
* @return new instance of {@link MongoJsonSchemaBuilder}.
|
||||
*/
|
||||
public static MongoJsonSchemaBuilder builder() {
|
||||
return new MongoJsonSchemaBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MongoJsonSchemaBuilder} provides a fluent API for defining a {@link MongoJsonSchema}.
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class MongoJsonSchemaBuilder {
|
||||
|
||||
private ObjectJsonSchemaObject root;
|
||||
|
||||
MongoJsonSchemaBuilder() {
|
||||
root = new ObjectJsonSchemaObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nrProperties
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#minNrProperties(int)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder minNrProperties(int nrProperties) {
|
||||
root = root.minNrProperties(nrProperties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nrProperties
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#maxNrProperties(int)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder maxNrProperties(int nrProperties) {
|
||||
root = root.maxNrProperties(nrProperties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties
|
||||
* @return
|
||||
* @see ObjectJsonSchemaObject#required(String...)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder required(String... properties) {
|
||||
root = root.required(properties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param additionalPropertiesAllowed
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#additionalProperties(boolean)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder additionalProperties(boolean additionalPropertiesAllowed) {
|
||||
root = root.additionalProperties(additionalPropertiesAllowed);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param schema
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#additionalProperties(ObjectJsonSchemaObject)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder additionalProperties(ObjectJsonSchemaObject schema) {
|
||||
root = root.additionalProperties(schema);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#properties(JsonSchemaProperty...)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder properties(JsonSchemaProperty... properties) {
|
||||
root = root.properties(properties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#patternProperties(JsonSchemaProperty...)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder patternProperties(JsonSchemaProperty... properties) {
|
||||
root = root.patternProperties(properties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param property
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#property(JsonSchemaProperty)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder property(JsonSchemaProperty property) {
|
||||
root = root.property(property);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder possibleValues(Set<Object> possibleValues) {
|
||||
root = root.possibleValues(possibleValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf
|
||||
* @return this
|
||||
* @see UntypedJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder allOf(Set<JsonSchemaObject> allOf) {
|
||||
root = root.allOf(allOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf
|
||||
* @return this
|
||||
* @see UntypedJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder anyOf(Set<JsonSchemaObject> anyOf) {
|
||||
root = root.anyOf(anyOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf
|
||||
* @return this
|
||||
* @see UntypedJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder oneOf(Set<JsonSchemaObject> oneOf) {
|
||||
root = root.oneOf(oneOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notMatch
|
||||
* @return this
|
||||
* @see UntypedJsonSchemaObject#notMatch(JsonSchemaObject)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder notMatch(JsonSchemaObject notMatch) {
|
||||
root = root.notMatch(notMatch);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description
|
||||
* @return this
|
||||
* @see UntypedJsonSchemaObject#description(String)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder description(String description) {
|
||||
root = root.description(description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the {@link MongoJsonSchema}.
|
||||
*
|
||||
* @return new instance of {@link MongoJsonSchema}.
|
||||
*/
|
||||
public MongoJsonSchema build() {
|
||||
return MongoJsonSchema.of(root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.schema;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Common base for {@link JsonSchemaObject} with shared types and {@link JsonSchemaObject#toDocument()} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
|
||||
protected final @Nullable String description;
|
||||
protected final Restrictions restrictions;
|
||||
protected final boolean generateDescription;
|
||||
|
||||
protected UntypedJsonSchemaObject(Restrictions restrictions, @Nullable String description, boolean generateDescription) {
|
||||
|
||||
this.description = description;
|
||||
this.restrictions = restrictions != null ? restrictions : Restrictions.empty();
|
||||
this.generateDescription = generateDescription;
|
||||
}
|
||||
|
||||
public static UntypedJsonSchemaObject newInstance() {
|
||||
return new UntypedJsonSchemaObject(null, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Type> getTypes() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@literal description}.
|
||||
*
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
public UntypedJsonSchemaObject description(String description) {
|
||||
return new UntypedJsonSchemaObject(restrictions, description, generateDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto generate the {@literal description} if not explicitly set.
|
||||
*
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
public UntypedJsonSchemaObject generatedDescription() {
|
||||
return new UntypedJsonSchemaObject(restrictions, description, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@literal enum}erates all possible values of the field.
|
||||
*
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
public UntypedJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
return new UntypedJsonSchemaObject(restrictions.possibleValues(possibleValues), description, generateDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* The field value must match all specified schemas.
|
||||
*
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
public UntypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
|
||||
return new UntypedJsonSchemaObject(restrictions.allOf(allOf), description, generateDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* The field value must match at least one of the specified schemas.
|
||||
*
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
public UntypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
|
||||
return new UntypedJsonSchemaObject(restrictions.anyOf(anyOf), description, generateDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* The field value must match exactly one of the specified schemas.
|
||||
*
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
public UntypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
|
||||
return new UntypedJsonSchemaObject(restrictions.oneOf(oneOf), description, generateDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* The field value must not match the specified schemas.
|
||||
*
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
public UntypedJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
|
||||
return new UntypedJsonSchemaObject(restrictions.notMatch(notMatch), description, generateDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the json schema complying {@link Document} representation. This includes {@literal type},
|
||||
* {@literal description} and the fields of {@link Restrictions#toDocument()} if set.
|
||||
*/
|
||||
@Override
|
||||
public Document toDocument() {
|
||||
|
||||
Document document = new Document();
|
||||
|
||||
getOrCreateDescription().ifPresent(val -> document.append("description", val));
|
||||
|
||||
if (restrictions != null) {
|
||||
document.putAll(restrictions.toDocument());
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private Optional<String> getOrCreateDescription() {
|
||||
|
||||
if (description != null) {
|
||||
return description.isEmpty() ? Optional.empty() : Optional.of(description);
|
||||
}
|
||||
|
||||
return generateDescription ? Optional.ofNullable(generateDescription()) : Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook for creating description out of defined values.<br />
|
||||
* Called by {@link #toDocument()} when no explicit {@link #description} is set.
|
||||
*
|
||||
* @return can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
protected String generateDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Restrictions} encapsulate common json schema restrictions like {@literal enum}, {@literal allOf}, ...
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
static class Restrictions {
|
||||
|
||||
private final Collection<Object> possibleValues;
|
||||
private final Collection<JsonSchemaObject> allOf;
|
||||
private final Collection<JsonSchemaObject> anyOf;
|
||||
private final Collection<JsonSchemaObject> oneOf;
|
||||
private final @Nullable JsonSchemaObject notMatch;
|
||||
|
||||
Restrictions(Collection<Object> possibleValues, Collection<JsonSchemaObject> allOf,
|
||||
Collection<JsonSchemaObject> anyOf, Collection<JsonSchemaObject> oneOf, @Nullable JsonSchemaObject notMatch) {
|
||||
|
||||
this.possibleValues = possibleValues;
|
||||
this.allOf = allOf;
|
||||
this.anyOf = anyOf;
|
||||
this.oneOf = oneOf;
|
||||
this.notMatch = notMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new empty {@link Restrictions}.
|
||||
*/
|
||||
static Restrictions empty() {
|
||||
|
||||
return new Restrictions(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(),
|
||||
Collections.emptySet(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Restrictions possibleValues(Collection<Object> possibleValues) {
|
||||
|
||||
Assert.notNull(possibleValues, "PossibleValues must not be null!");
|
||||
return new Restrictions(possibleValues, allOf, anyOf, oneOf, notMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Restrictions allOf(Collection<JsonSchemaObject> allOf) {
|
||||
|
||||
Assert.notNull(allOf, "AllOf must not be null!");
|
||||
return new Restrictions(possibleValues, allOf, anyOf, oneOf, notMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Restrictions anyOf(Collection<JsonSchemaObject> anyOf) {
|
||||
|
||||
Assert.notNull(anyOf, "AnyOf must not be null!");
|
||||
return new Restrictions(possibleValues, allOf, anyOf, oneOf, notMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Restrictions oneOf(Collection<JsonSchemaObject> oneOf) {
|
||||
|
||||
Assert.notNull(oneOf, "OneOf must not be null!");
|
||||
return new Restrictions(possibleValues, allOf, anyOf, oneOf, notMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notMatch must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Restrictions notMatch(JsonSchemaObject notMatch) {
|
||||
|
||||
Assert.notNull(notMatch, "NotMatch must not be null!");
|
||||
return new Restrictions(possibleValues, allOf, anyOf, oneOf, notMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the json schema complying {@link Document} representation. This includes {@literal enum},
|
||||
* {@literal allOf}, {@literal anyOf}, {@literal oneOf}, {@literal notMatch} if set.
|
||||
*
|
||||
* @return never {@literal null}
|
||||
*/
|
||||
Document toDocument() {
|
||||
|
||||
Document document = new Document();
|
||||
|
||||
if (!CollectionUtils.isEmpty(possibleValues)) {
|
||||
document.append("enum", possibleValues);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(allOf)) {
|
||||
document.append("allOf", allOf.stream().map(JsonSchemaObject::toDocument).collect(Collectors.toList()));
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(anyOf)) {
|
||||
document.append("anyOf", anyOf.stream().map(JsonSchemaObject::toDocument).collect(Collectors.toList()));
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(oneOf)) {
|
||||
document.append("oneOf", oneOf.stream().map(JsonSchemaObject::toDocument).collect(Collectors.toList()));
|
||||
}
|
||||
if (notMatch != null) {
|
||||
document.append("not", notMatch.toDocument());
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.mongodb.core.schema;
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*;
|
||||
|
||||
import lombok.Data;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
|
||||
import org.springframework.data.mongodb.test.util.MongoVersionRule;
|
||||
import org.springframework.data.util.Version;
|
||||
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.reactivestreams.client.MongoClients;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class JsonSchemaQueryTests {
|
||||
|
||||
public static final String DATABASE_NAME = "json-schema-query-tests";
|
||||
|
||||
public static @ClassRule MongoVersionRule REQUIRES_AT_LEAST_3_6_0 = MongoVersionRule.atLeast(Version.parse("3.6.0"));
|
||||
|
||||
MongoTemplate template;
|
||||
Person jellyBelly, roseSpringHeart, kazmardBoombub;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
template = new MongoTemplate(new MongoClient(), DATABASE_NAME);
|
||||
|
||||
jellyBelly = new Person();
|
||||
jellyBelly.id = "1";
|
||||
jellyBelly.name = "Jelly Belly";
|
||||
jellyBelly.gender = Gender.PIXY;
|
||||
jellyBelly.address = new Address();
|
||||
jellyBelly.address.city = "Candy Hill";
|
||||
jellyBelly.address.street = "Apple Mint Street";
|
||||
jellyBelly.value = 42;
|
||||
|
||||
roseSpringHeart = new Person();
|
||||
roseSpringHeart.id = "2";
|
||||
roseSpringHeart.name = "Rose SpringHeart";
|
||||
roseSpringHeart.gender = Gender.UNICORN;
|
||||
roseSpringHeart.address = new Address();
|
||||
roseSpringHeart.address.city = "Rainbow Valley";
|
||||
roseSpringHeart.address.street = "Twinkle Ave.";
|
||||
roseSpringHeart.value = 42L;
|
||||
|
||||
kazmardBoombub = new Person();
|
||||
kazmardBoombub.id = "3";
|
||||
kazmardBoombub.name = "Kazmard Boombub";
|
||||
kazmardBoombub.gender = Gender.GOBLIN;
|
||||
kazmardBoombub.value = "green";
|
||||
|
||||
template.save(jellyBelly);
|
||||
template.save(roseSpringHeart);
|
||||
template.save(kazmardBoombub);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void findsDocumentsWithRequiredFieldsCorrectly() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().required("address").build();
|
||||
|
||||
assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class))
|
||||
.containsExactlyInAnyOrder(jellyBelly, roseSpringHeart);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void findsDocumentsWithRequiredFieldsReactively() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().required("address").build();
|
||||
|
||||
StepVerifier.create(new ReactiveMongoTemplate(MongoClients.create(), DATABASE_NAME)
|
||||
.find(query(matchingDocumentStructure(schema)), Person.class)).expectNextCount(2).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void findsDocumentsWithBsonFieldTypesCorrectly() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().property(int32("value")).build();
|
||||
|
||||
assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class))
|
||||
.containsExactlyInAnyOrder(jellyBelly);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void findsDocumentsWithJsonFieldTypesCorrectly() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().property(number("value")).build();
|
||||
|
||||
assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class))
|
||||
.containsExactlyInAnyOrder(jellyBelly, roseSpringHeart);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void combineSchemaWithOtherCriteria() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().property(number("value")).build();
|
||||
|
||||
assertThat(
|
||||
template.find(query(matchingDocumentStructure(schema).and("name").is(roseSpringHeart.name)), Person.class))
|
||||
.containsExactlyInAnyOrder(roseSpringHeart);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void usesMappedFieldNameForRequiredProperties() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build();
|
||||
|
||||
assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class))
|
||||
.containsExactlyInAnyOrder(jellyBelly, roseSpringHeart, kazmardBoombub);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void usesMappedFieldNameForProperties() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().property(string("name").matching("^R.*")).build();
|
||||
|
||||
assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class))
|
||||
.containsExactlyInAnyOrder(roseSpringHeart);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void mapsNestedFieldName() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder() //
|
||||
.required("address") //
|
||||
.property(object("address").properties(string("street").matching("^Apple.*"))).build();
|
||||
|
||||
assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class))
|
||||
.containsExactlyInAnyOrder(jellyBelly);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void mapsEnumValuesCorrectly() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder()
|
||||
.property(untyped("gender").possibleValues(Gender.PIXY, Gender.GOBLIN)).build();
|
||||
|
||||
assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class))
|
||||
.containsExactlyInAnyOrder(jellyBelly, kazmardBoombub);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void useTypeOperatorOnFieldLevel() {
|
||||
assertThat(template.find(query(where("value").type(Type.intType())), Person.class)).containsExactly(jellyBelly);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void useTypeOperatorWithMultipleTypesOnFieldLevel() {
|
||||
|
||||
assertThat(template.find(query(where("value").type(Type.intType(), Type.stringType())), Person.class))
|
||||
.containsExactlyInAnyOrder(jellyBelly, kazmardBoombub);
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Person {
|
||||
|
||||
@Id String id;
|
||||
|
||||
@Field("full_name") String name;
|
||||
Gender gender;
|
||||
Address address;
|
||||
Object value;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Address {
|
||||
|
||||
String city;
|
||||
|
||||
@Field("str") String street;
|
||||
}
|
||||
|
||||
static enum Gender {
|
||||
PIXY, UNICORN, GOBLIN
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.convert;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*;
|
||||
import static org.springframework.data.mongodb.test.util.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MongoJsonSchemaMapper}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MongoJsonSchemaMapperUnitTests {
|
||||
|
||||
public @Rule ExpectedException exception = ExpectedException.none();
|
||||
|
||||
MongoJsonSchemaMapper mapper;
|
||||
|
||||
Document addressProperty = new Document("type", "object").append("required", Arrays.asList("street", "postCode"))
|
||||
.append("properties",
|
||||
new Document("street", new Document("type", "string")).append("postCode", new Document("type", "string")));
|
||||
|
||||
Document mappedAddressProperty = new Document("type", "object")
|
||||
.append("required", Arrays.asList("street", "post_code")).append("properties",
|
||||
new Document("street", new Document("type", "string")).append("post_code", new Document("type", "string")));
|
||||
|
||||
Document nameProperty = new Document("type", "string");
|
||||
Document gradePointAverageProperty = new Document("bsonType", "double");
|
||||
Document yearProperty = new Document("bsonType", "int").append("minimum", 2017).append("maximum", 3017)
|
||||
.append("exclusiveMaximum", true);
|
||||
|
||||
Document properties = new Document("name", nameProperty) //
|
||||
.append("gradePointAverage", gradePointAverageProperty) //
|
||||
.append("year", yearProperty);
|
||||
|
||||
Document mappedProperties = new Document("name", new Document(nameProperty)) //
|
||||
.append("gpa", new Document(gradePointAverageProperty)) //
|
||||
.append("year", new Document(yearProperty));
|
||||
|
||||
List<String> requiredProperties = Arrays.asList("name", "gradePointAverage");
|
||||
List<String> mappedRequiredProperties = Arrays.asList("name", "gpa");
|
||||
|
||||
Document $jsonSchema = new Document("type", "object") //
|
||||
.append("required", requiredProperties) //
|
||||
.append("properties", properties);
|
||||
|
||||
Document mapped$jsonSchema = new Document("type", "object") //
|
||||
.append("required", mappedRequiredProperties) //
|
||||
.append("properties", mappedProperties);
|
||||
|
||||
Document sourceSchemaDocument = new Document("$jsonSchema", $jsonSchema);
|
||||
Document mappedSchemaDocument = new Document("$jsonSchema", mapped$jsonSchema);
|
||||
|
||||
String complexSchemaJsonString = "{ $jsonSchema: {" + //
|
||||
" type: \"object\"," + //
|
||||
" required: [ \"name\", \"year\", \"major\", \"gpa\" ]," + //
|
||||
" properties: {" + //
|
||||
" name: {" + //
|
||||
" type: \"string\"," + //
|
||||
" description: \"must be a string and is required\"" + //
|
||||
" }," + //
|
||||
" gender: {" + //
|
||||
" type: \"string\"," + //
|
||||
" description: \"must be a string and is not required\"" + //
|
||||
" }," + //
|
||||
" year: {" + //
|
||||
" bsonType: \"int\"," + //
|
||||
" minimum: 2017," + //
|
||||
" maximum: 3017," + //
|
||||
" exclusiveMaximum: true," + //
|
||||
" description: \"must be an integer in [ 2017, 3017 ] and is required\"" + //
|
||||
" }," + //
|
||||
" major: {" + //
|
||||
" type: \"string\"," + //
|
||||
" enum: [ \"Math\", \"English\", \"Computer Science\", \"History\", null ]," + //
|
||||
" description: \"can only be one of the enum values and is required\"" + //
|
||||
" }," + //
|
||||
" gpa: {" + //
|
||||
" bsonType: \"double\"," + //
|
||||
" description: \"must be a double and is required\"" + //
|
||||
" }" + //
|
||||
" }" + //
|
||||
" } }";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mapper = new MongoJsonSchemaMapper(new MappingMongoConverter(mock(DbRefResolver.class), new MongoMappingContext()));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void noNullSchemaAllowed() {
|
||||
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
|
||||
mapper.mapSchema(null, Object.class);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void noNullDomainTypeAllowed() {
|
||||
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
|
||||
mapper.mapSchema(new Document("$jsonSchema", new Document()), null);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void schemaDocumentMustContain$jsonSchemaField() {
|
||||
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage("contain $jsonSchema");
|
||||
|
||||
mapper.mapSchema(new Document("foo", new Document()), Object.class);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectTypeSkipsFieldMapping() {
|
||||
assertThat(mapper.mapSchema(sourceSchemaDocument, Object.class)).isEqualTo(sourceSchemaDocument);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void mapSchemaProducesNewDocument() {
|
||||
assertThat(mapper.mapSchema(sourceSchemaDocument, Object.class)).isNotSameAs(sourceSchemaDocument);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void mapSchemaMapsPropertiesToFieldNames() {
|
||||
assertThat(mapper.mapSchema(sourceSchemaDocument, Student.class)).isEqualTo(mappedSchemaDocument);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void mapSchemaLeavesSourceDocumentUntouched() {
|
||||
|
||||
Document source = Document.parse(complexSchemaJsonString);
|
||||
mapper.mapSchema(source, Student.class);
|
||||
|
||||
assertThat(source).isEqualTo(Document.parse(complexSchemaJsonString));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void mapsNestedPropertiesCorrectly() {
|
||||
|
||||
Document schema = new Document("$jsonSchema", new Document("type", "object") //
|
||||
.append("properties", new Document(properties).append("address", addressProperty)));
|
||||
|
||||
Document expectedSchema = new Document("$jsonSchema", new Document("type", "object") //
|
||||
.append("properties", new Document(mappedProperties).append("address", mappedAddressProperty)));
|
||||
|
||||
assertThat(mapper.mapSchema(schema, Student.class)).isEqualTo(expectedSchema);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void constructReferenceSchemaCorrectly() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder() //
|
||||
.required("name", "year", "major", "gradePointAverage").description("") //
|
||||
.properties(string("name").description("must be a string and is required"), //
|
||||
string("gender").description("must be a string and is not required"), //
|
||||
int32("year").description("must be an integer in [ 2017, 3017 ] and is required").gte(2017).lt(3017), //
|
||||
string("major").description("can only be one of the enum values and is required").possibleValues("Math",
|
||||
"English", "Computer Science", "History", null), //
|
||||
float64("gradePointAverage").description("must be a double and is required") //
|
||||
).build();
|
||||
|
||||
assertThat(mapper.mapSchema(schema.toDocument(), Student.class)).isEqualTo(Document.parse(complexSchemaJsonString));
|
||||
}
|
||||
|
||||
static class Student {
|
||||
|
||||
String name;
|
||||
Gender gender;
|
||||
Integer year;
|
||||
String major;
|
||||
|
||||
@Field("gpa") //
|
||||
Double gradePointAverage;
|
||||
Address address;
|
||||
}
|
||||
|
||||
static class Address {
|
||||
|
||||
String city;
|
||||
String street;
|
||||
|
||||
@Field("post_code") //
|
||||
String postCode;
|
||||
}
|
||||
|
||||
static enum Gender {
|
||||
M, F
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,12 +19,15 @@ import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonLineString;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
@@ -213,4 +216,24 @@ public class CriteriaTests {
|
||||
|
||||
assertThat(document, isBsonObject().containing("foo.$geoIntersects.$geometry", lineString));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void extractsJsonSchemaInChainCorrectly() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build();
|
||||
Criteria critera = Criteria.where("foo").is("bar").andDocumentStructureMatches(schema);
|
||||
|
||||
assertThat(critera.getCriteriaObject(), is(equalTo(new Document("foo", "bar").append("$jsonSchema",
|
||||
new Document("type", "object").append("required", Collections.singletonList("name"))))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void extractsJsonSchemaFromFactoryMethodCorrectly() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build();
|
||||
Criteria critera = Criteria.matchingDocumentStructure(schema);
|
||||
|
||||
assertThat(critera.getCriteriaObject(), is(equalTo(new Document("$jsonSchema",
|
||||
new Document("type", "object").append("required", Collections.singletonList("name"))))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.schema;
|
||||
|
||||
import static org.springframework.data.domain.Range.from;
|
||||
import static org.springframework.data.domain.Range.Bound.*;
|
||||
import static org.springframework.data.mongodb.core.schema.JsonSchemaObject.*;
|
||||
import static org.springframework.data.mongodb.core.schema.JsonSchemaObject.of;
|
||||
import static org.springframework.data.mongodb.test.util.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.domain.Range.*;
|
||||
|
||||
/**
|
||||
* Tests verifying {@link org.bson.Document} representation of {@link JsonSchemaObject}s.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class JsonSchemaObjectUnitTests {
|
||||
|
||||
// -----------------
|
||||
// type : 'object'
|
||||
// -----------------
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectObjectShouldRenderTypeCorrectly() {
|
||||
|
||||
assertThat(object().generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "object").append("description", "Must be an object."));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectObjectShouldRenderNrPropertiesCorrectly() {
|
||||
|
||||
assertThat(object().nrProperties(from(inclusive(10)).to(inclusive(20))).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "object").append("description", "Must be an object with [10-20] properties.")
|
||||
.append("minProperties", 10).append("maxProperties", 20));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectObjectShouldRenderRequiredPropertiesCorrectly() {
|
||||
|
||||
assertThat(object().required("spring", "data", "mongodb").generatedDescription().toDocument()).isEqualTo(
|
||||
new Document("type", "object")
|
||||
.append("description", "Must be an object where spring, data, mongodb are mandatory.").append("required",
|
||||
Arrays.asList("spring", "data", "mongodb")));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectObjectShouldRenderAdditionalPropertiesCorrectlyWhenBoolean() {
|
||||
|
||||
assertThat(object().additionalProperties(true).generatedDescription().toDocument()).isEqualTo(
|
||||
new Document("type", "object").append("description", "Must be an object allowing additional properties.")
|
||||
.append("additionalProperties", true));
|
||||
|
||||
assertThat(object().additionalProperties(false).generatedDescription().toDocument()).isEqualTo(
|
||||
new Document("type", "object").append("description", "Must be an object not allowing additional properties.")
|
||||
.append("additionalProperties", false));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectObjectShouldRenderPropertiesCorrectly() {
|
||||
|
||||
Document expected = new Document("type", "object")
|
||||
.append("description", "Must be an object defining restrictions for name, active.").append("properties",
|
||||
new Document("name", new Document("type", "string")
|
||||
.append("description", "Must be a string with length unbounded-10].").append("maxLength", 10))
|
||||
.append("active", new Document("type", "boolean")));
|
||||
|
||||
assertThat(object().generatedDescription()
|
||||
.properties(JsonSchemaProperty.string("name").maxLength(10).generatedDescription(),
|
||||
JsonSchemaProperty.bool("active"))
|
||||
.generatedDescription().toDocument()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectObjectShouldRenderNestedObjectPropertiesCorrectly() {
|
||||
|
||||
Document expected = new Document("type", "object")
|
||||
.append("description", "Must be an object defining restrictions for address.")
|
||||
.append("properties", new Document("address",
|
||||
new Document("type", "object").append("description", "Must be an object defining restrictions for city.")
|
||||
.append("properties", new Document("city", new Document("type", "string")
|
||||
.append("description", "Must be a string with length [3-unbounded.").append("minLength", 3)))));
|
||||
|
||||
assertThat(object()
|
||||
.properties(JsonSchemaProperty.object("address")
|
||||
.properties(JsonSchemaProperty.string("city").minLength(3).generatedDescription()).generatedDescription())
|
||||
.generatedDescription().toDocument()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectObjectShouldRenderPatternPropertiesCorrectly() {
|
||||
|
||||
Document expected = new Document("type", "object")
|
||||
.append("description", "Must be an object defining restrictions for patterns na.*.")
|
||||
.append("patternProperties", new Document("na.*", new Document("type", "string")
|
||||
.append("description", "Must be a string with length unbounded-10].").append("maxLength", 10)));
|
||||
|
||||
assertThat(object().patternProperties(JsonSchemaProperty.string("na.*").maxLength(10).generatedDescription())
|
||||
.generatedDescription().toDocument()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// type : 'string'
|
||||
// -----------------
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void stringObjectShouldRenderTypeCorrectly() {
|
||||
|
||||
assertThat(string().generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "string").append("description", "Must be a string."));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void stringObjectShouldRenderDescriptionCorrectly() {
|
||||
|
||||
assertThat(string().description("error msg").toDocument())
|
||||
.isEqualTo(new Document("type", "string").append("description", "error msg"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void stringObjectShouldRenderRangeCorrectly() {
|
||||
|
||||
assertThat(string().length(from(inclusive(10)).to(inclusive(20))).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "string").append("description", "Must be a string with length [10-20].")
|
||||
.append("minLength", 10).append("maxLength", 20));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void stringObjectShouldRenderPatternCorrectly() {
|
||||
|
||||
assertThat(string().matching("^spring$").generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "string").append("description", "Must be a string matching ^spring$.")
|
||||
.append("pattern", "^spring$"));
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// type : 'number'
|
||||
// -----------------
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void numberObjectShouldRenderMultipleOfCorrectly() {
|
||||
|
||||
assertThat(number().multipleOf(3.141592F).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "number").append("description", "Must be a numeric value multiple of 3.141592.")
|
||||
.append("multipleOf", 3.141592F));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void numberObjectShouldRenderMaximumCorrectly() {
|
||||
|
||||
assertThat(
|
||||
number().within(Range.of(Bound.unbounded(), Bound.inclusive(3.141592F))).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "number")
|
||||
.append("description", "Must be a numeric value within range unbounded-3.141592].")
|
||||
.append("maximum", 3.141592F));
|
||||
|
||||
assertThat(
|
||||
number().within(Range.of(Bound.unbounded(), Bound.exclusive(3.141592F))).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "number")
|
||||
.append("description", "Must be a numeric value within range unbounded-3.141592).")
|
||||
.append("maximum", 3.141592F).append("exclusiveMaximum", true));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void numberObjectShouldRenderMinimumCorrectly() {
|
||||
|
||||
assertThat(
|
||||
number().within(Range.of(Bound.inclusive(3.141592F), Bound.unbounded())).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "number")
|
||||
.append("description", "Must be a numeric value within range [3.141592-unbounded.")
|
||||
.append("minimum", 3.141592F));
|
||||
|
||||
assertThat(
|
||||
number().within(Range.of(Bound.exclusive(3.141592F), Bound.unbounded())).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "number")
|
||||
.append("description", "Must be a numeric value within range (3.141592-unbounded.")
|
||||
.append("minimum", 3.141592F).append("exclusiveMinimum", true));
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// type : 'arrays'
|
||||
// -----------------
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void arrayObjectShouldRenderItemsCorrectly() {
|
||||
|
||||
assertThat(array().items(Arrays.asList(string(), bool())).toDocument()).isEqualTo(new Document("type", "array")
|
||||
.append("items", Arrays.asList(new Document("type", "string"), new Document("type", "boolean"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void arrayObjectShouldRenderMaxItemsCorrectly() {
|
||||
|
||||
assertThat(array().maxItems(5).generatedDescription().toDocument()).isEqualTo(new Document("type", "array")
|
||||
.append("description", "Must be an array having size unbounded-5].").append("maxItems", 5));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void arrayObjectShouldRenderMinItemsCorrectly() {
|
||||
|
||||
assertThat(array().minItems(5).generatedDescription().toDocument()).isEqualTo(new Document("type", "array")
|
||||
.append("description", "Must be an array having size [5-unbounded.").append("minItems", 5));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void arrayObjectShouldRenderUniqueItemsCorrectly() {
|
||||
|
||||
assertThat(array().uniqueItems(true).generatedDescription().toDocument()).isEqualTo(new Document("type", "array")
|
||||
.append("description", "Must be an array of unique values.").append("uniqueItems", true));
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// type : 'any'
|
||||
// -----------------
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void typedObjectShouldRenderEnumCorrectly() {
|
||||
|
||||
assertThat(of(String.class).possibleValues(Arrays.asList("one", "two")).toDocument())
|
||||
.isEqualTo(new Document("type", "string").append("enum", Arrays.asList("one", "two")));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void typedObjectShouldRenderAllOfCorrectly() {
|
||||
|
||||
assertThat(of(Object.class).allOf(Arrays.asList(string())).toDocument())
|
||||
.isEqualTo(new Document("type", "object").append("allOf", Arrays.asList(new Document("type", "string"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void typedObjectShouldRenderAnyOfCorrectly() {
|
||||
|
||||
assertThat(of(String.class).anyOf(Arrays.asList(string())).toDocument())
|
||||
.isEqualTo(new Document("type", "string").append("anyOf", Arrays.asList(new Document("type", "string"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void typedObjectShouldRenderOneOfCorrectly() {
|
||||
|
||||
assertThat(of(String.class).oneOf(Arrays.asList(string())).toDocument())
|
||||
.isEqualTo(new Document("type", "string").append("oneOf", Arrays.asList(new Document("type", "string"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void typedObjectShouldRenderNotCorrectly() {
|
||||
|
||||
assertThat(untyped().notMatch(string()).toDocument())
|
||||
.isEqualTo(new Document("not", new Document("type", "string")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.schema;
|
||||
|
||||
import static org.springframework.data.mongodb.test.util.Assertions.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
|
||||
import org.springframework.data.mongodb.core.CollectionOptions;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.test.util.MongoVersionRule;
|
||||
import org.springframework.data.util.Version;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.CreateCollectionOptions;
|
||||
import com.mongodb.client.model.ValidationAction;
|
||||
import com.mongodb.client.model.ValidationLevel;
|
||||
import com.mongodb.client.model.ValidationOptions;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2017/12
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class MongoJsonSchemaTests {
|
||||
|
||||
public static @ClassRule MongoVersionRule REQUIRES_AT_LEAST_3_6_0 = MongoVersionRule.atLeast(Version.parse("3.6.0"));
|
||||
|
||||
@Configuration
|
||||
static class Config extends AbstractMongoConfiguration {
|
||||
|
||||
@Override
|
||||
public MongoClient mongoClient() {
|
||||
return new MongoClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDatabaseName() {
|
||||
return "json-schema-tests";
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired MongoTemplate template;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
template.dropCollection(Person.class);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void writeSchemaViaTemplate() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder() //
|
||||
.required("firstname", "lastname") //
|
||||
.properties( //
|
||||
JsonSchemaProperty.string("firstname").possibleValues("luke", "han").maxLength(10), //
|
||||
JsonSchemaProperty.object("address") //
|
||||
.properties(JsonSchemaProperty.string("postCode").minLength(4).maxLength(5))
|
||||
|
||||
).build();
|
||||
|
||||
template.createCollection(Person.class, CollectionOptions.empty().schema(schema));
|
||||
|
||||
Document $jsonSchema = new MongoJsonSchemaMapper(template.getConverter()).mapSchema(schema.toDocument(),
|
||||
Person.class);
|
||||
|
||||
Document fromDb = readSchemaFromDatabase("persons");
|
||||
assertThat(fromDb).isEqualTo($jsonSchema);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void nonMappedSchema() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder() //
|
||||
.required("firstname", "lastname") //
|
||||
.properties( //
|
||||
JsonSchemaProperty.string("firstname").possibleValues("luke", "han").maxLength(10), //
|
||||
JsonSchemaProperty.object("address") //
|
||||
.properties(JsonSchemaProperty.string("postCode").minLength(4).maxLength(5))
|
||||
|
||||
).build();
|
||||
|
||||
template.createCollection("persons", CollectionOptions.empty().schema(schema));
|
||||
|
||||
Document fromDb = readSchemaFromDatabase("persons");
|
||||
assertThat(fromDb)
|
||||
.isNotEqualTo(new MongoJsonSchemaMapper(template.getConverter()).mapSchema(schema.toDocument(), Person.class));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void writeSchemaManually() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder() //
|
||||
.required("firstname", "lastname") //
|
||||
.properties( //
|
||||
JsonSchemaProperty.string("firstname").possibleValues("luke", "han").maxLength(10), //
|
||||
JsonSchemaProperty.object("address") //
|
||||
.properties(JsonSchemaProperty.string("postCode").minLength(4).maxLength(5))
|
||||
|
||||
).build();
|
||||
|
||||
Document $jsonSchema = new MongoJsonSchemaMapper(template.getConverter()).mapSchema(schema.toDocument(),
|
||||
Person.class);
|
||||
|
||||
ValidationOptions options = new ValidationOptions();
|
||||
options.validationLevel(ValidationLevel.MODERATE);
|
||||
options.validationAction(ValidationAction.ERROR);
|
||||
options.validator($jsonSchema);
|
||||
|
||||
CreateCollectionOptions cco = new CreateCollectionOptions();
|
||||
cco.validationOptions(options);
|
||||
|
||||
MongoDatabase db = template.getDb();
|
||||
db.createCollection("persons", cco);
|
||||
|
||||
Document fromDb = readSchemaFromDatabase("persons");
|
||||
assertThat(fromDb).isEqualTo($jsonSchema);
|
||||
}
|
||||
|
||||
Document readSchemaFromDatabase(String collectionName) {
|
||||
|
||||
Document collectionInfo = template
|
||||
.executeCommand(new Document("listCollections", 1).append("filter", new Document("name", collectionName)));
|
||||
|
||||
if (collectionInfo == null) {
|
||||
throw new DataRetrievalFailureException(String.format("Collection %s was not found."));
|
||||
}
|
||||
|
||||
if (collectionInfo.containsKey("cursor")) {
|
||||
collectionInfo = (Document) collectionInfo.get("cursor", Document.class).get("firstBatch", List.class).iterator()
|
||||
.next();
|
||||
}
|
||||
|
||||
if (!collectionInfo.containsKey("options")) {
|
||||
return new Document();
|
||||
}
|
||||
|
||||
return collectionInfo.get("options", Document.class).get("validator", Document.class);
|
||||
}
|
||||
|
||||
@Data
|
||||
@org.springframework.data.mongodb.core.mapping.Document(collection = "persons")
|
||||
static class Person {
|
||||
|
||||
@Field("first_name") String firstname;
|
||||
String lastname;
|
||||
Address address;
|
||||
|
||||
}
|
||||
|
||||
static class Address {
|
||||
|
||||
String city;
|
||||
String street;
|
||||
|
||||
@Field("post_code") String postCode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2018 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.mongodb.core.schema;
|
||||
|
||||
import static org.springframework.data.mongodb.test.util.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MongoJsonSchemaUnitTests {
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void toDocumentRendersSchemaCorrectly() {
|
||||
|
||||
MongoJsonSchema schema = MongoJsonSchema.builder() //
|
||||
.required("firstname", "lastname") //
|
||||
.build();
|
||||
|
||||
assertThat(schema.toDocument()).isEqualTo(new Document("$jsonSchema",
|
||||
new Document("type", "object").append("required", Arrays.asList("firstname", "lastname"))));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-1835
|
||||
public void throwsExceptionOnNullRoot() {
|
||||
MongoJsonSchema.of(null);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
== What's new in Spring Data MongoDB 2.1
|
||||
* Cursor-based aggregation execution.
|
||||
* <<mongo-template.query.distinct,Distinct queries>> for imperative and reactive Template API.
|
||||
* `validator` support for collections.
|
||||
* `$jsonSchema` support for queries.
|
||||
|
||||
[[new-features.2-0-0]]
|
||||
== What's new in Spring Data MongoDB 2.0
|
||||
|
||||
@@ -1464,6 +1464,33 @@ AggregationResults<TagCount> results = template.aggregate(aggregation, "tags", T
|
||||
|
||||
WARNING: Indexes are only used if the collation used for the operation and the index collation matches.
|
||||
|
||||
[[mongo.jsonSchema]]
|
||||
=== JSON Schema
|
||||
|
||||
As of version 3.6 MongoDB supports collections that validate ``Document``s against a provided JSON Schema. The schema itself and both validation action and level can be defined when creating the collection.
|
||||
|
||||
`CollectionOptions` provides the entry point to schema support for collections.
|
||||
|
||||
.Create collection with `$jsonSchema`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
// TODO: add sample here!
|
||||
----
|
||||
====
|
||||
|
||||
Additionally it is also possible to query any collection for documents that match a given structure defined by a JSON Schema.
|
||||
|
||||
.Query collation for Documents matching a `$jsonSchema`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
// TODO: add sample here!
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: `$jsonSchema` can only be applied on the top level of a query and not property specific. Use the `properties` attribute of the schema to match against nested fields.
|
||||
|
||||
[[mongo.query.fluent-template-api]]
|
||||
=== Fluent Template API
|
||||
|
||||
|
||||
Reference in New Issue
Block a user