DATAMONGO-1835 - Polishing.
Add JSON schema to collection creation using reactive API. Refactor MongoJsonSchema to interface with default implementations for JsonSchemaObject and Document-based schemas. Make fields final and methods static where possible. Add minItems/maxItems/additionalItems properties to ArrayJsonSchemaProperty. Add missing overrides to NullJsonSchemaProperty. Slightly rename methods for item/property counts. Add generics, Javadoc, minor tweaks. Original pull request: #524.
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
@@ -146,7 +148,8 @@ public class CollectionOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to {@code off}.
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to
|
||||
* {@link ValidationLevel#OFF}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
@@ -156,7 +159,8 @@ public class CollectionOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to {@code strict}.
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to
|
||||
* {@link ValidationLevel#STRICT}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
@@ -167,7 +171,7 @@ public class CollectionOptions {
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to
|
||||
* {@code moderate}.
|
||||
* {@link ValidationLevel#MODERATE}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
@@ -177,7 +181,8 @@ public class CollectionOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to {@code warn}.
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to
|
||||
* {@link ValidationAction#WARN}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
@@ -187,7 +192,8 @@ public class CollectionOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to {@code error}.
|
||||
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to
|
||||
* {@link ValidationAction#ERROR}.
|
||||
*
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.1
|
||||
@@ -291,25 +297,18 @@ public class CollectionOptions {
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
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;
|
||||
}
|
||||
private final @Nullable MongoJsonSchema schema;
|
||||
private final @Nullable ValidationLevel validationLevel;
|
||||
private final @Nullable ValidationAction validationAction;
|
||||
|
||||
/**
|
||||
* Create an empty {@link Validator}.
|
||||
*
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static Validator none() {
|
||||
@@ -321,7 +320,6 @@ public class CollectionOptions {
|
||||
*
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
*/
|
||||
@Nullable
|
||||
public Optional<MongoJsonSchema> getSchema() {
|
||||
return Optional.ofNullable(schema);
|
||||
}
|
||||
@@ -331,7 +329,6 @@ public class CollectionOptions {
|
||||
*
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
*/
|
||||
@Nullable
|
||||
public Optional<ValidationLevel> getValidationLevel() {
|
||||
return Optional.ofNullable(validationLevel);
|
||||
}
|
||||
@@ -341,7 +338,6 @@ public class CollectionOptions {
|
||||
*
|
||||
* @return @return {@link Optional#empty()} if not set.
|
||||
*/
|
||||
@Nullable
|
||||
public Optional<ValidationAction> getValidationAction() {
|
||||
return Optional.ofNullable(validationAction);
|
||||
}
|
||||
|
||||
@@ -124,6 +124,9 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.ReadPreference;
|
||||
@@ -2370,7 +2373,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
Document doc = convertToDocument(collectionOptions);
|
||||
|
||||
if (collectionOptions.getValidator().isPresent()) {
|
||||
if (collectionOptions != null && collectionOptions.getValidator().isPresent()) {
|
||||
Validator v = collectionOptions.getValidator().get();
|
||||
v.getSchema().ifPresent(val -> doc.put("validator", schemaMapper.mapSchema(val.toDocument(), targetType)));
|
||||
}
|
||||
|
||||
@@ -69,15 +69,7 @@ import org.springframework.data.mongodb.core.aggregation.AggregationOperationCon
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
||||
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
|
||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefProxyHandler;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolverCallback;
|
||||
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.MongoWriter;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.convert.UpdateMapper;
|
||||
import org.springframework.data.mongodb.core.convert.*;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperationsAdapter;
|
||||
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
|
||||
@@ -125,6 +117,7 @@ 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.ValidationOptions;
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import com.mongodb.reactivestreams.client.AggregatePublisher;
|
||||
@@ -176,6 +169,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
private final QueryMapper queryMapper;
|
||||
private final UpdateMapper updateMapper;
|
||||
private final JsonSchemaMapper schemaMapper;
|
||||
private final SpelAwareProxyProjectionFactory projectionFactory;
|
||||
|
||||
private @Nullable WriteConcern writeConcern;
|
||||
@@ -220,6 +214,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter() : 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
|
||||
@@ -486,14 +481,15 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
*/
|
||||
public <T> Mono<MongoCollection<Document>> createCollection(Class<T> entityClass,
|
||||
@Nullable CollectionOptions collectionOptions) {
|
||||
return createCollection(determineCollectionName(entityClass), collectionOptions);
|
||||
return doCreateCollection(determineCollectionName(entityClass),
|
||||
convertToCreateCollectionOptions(collectionOptions, entityClass));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveMongoOperations#createCollection(java.lang.String)
|
||||
*/
|
||||
public Mono<MongoCollection<Document>> createCollection(final String collectionName) {
|
||||
public Mono<MongoCollection<Document>> createCollection(String collectionName) {
|
||||
return doCreateCollection(collectionName, new CreateCollectionOptions());
|
||||
}
|
||||
|
||||
@@ -501,8 +497,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveMongoOperations#createCollection(java.lang.String, org.springframework.data.mongodb.core.CollectionOptions)
|
||||
*/
|
||||
public Mono<MongoCollection<Document>> createCollection(final String collectionName,
|
||||
final CollectionOptions collectionOptions) {
|
||||
public Mono<MongoCollection<Document>> createCollection(String collectionName,
|
||||
@Nullable CollectionOptions collectionOptions) {
|
||||
return doCreateCollection(collectionName, convertToCreateCollectionOptions(collectionOptions));
|
||||
}
|
||||
|
||||
@@ -814,8 +810,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
}
|
||||
|
||||
ReadDocumentCallback<O> readCallback = new ReadDocumentCallback<>(mongoConverter, outputType, collectionName);
|
||||
return execute(collectionName,
|
||||
collection -> aggregateAndMap(collection, pipeline, options, readCallback));
|
||||
return execute(collectionName, collection -> aggregateAndMap(collection, pipeline, options, readCallback));
|
||||
}
|
||||
|
||||
private <O> Flux<O> aggregateAndMap(MongoCollection<Document> collection, List<Document> pipeline,
|
||||
@@ -1995,17 +1990,36 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
}
|
||||
|
||||
protected CreateCollectionOptions convertToCreateCollectionOptions(@Nullable CollectionOptions collectionOptions) {
|
||||
return convertToCreateCollectionOptions(collectionOptions, Object.class);
|
||||
}
|
||||
|
||||
protected CreateCollectionOptions convertToCreateCollectionOptions(@Nullable CollectionOptions collectionOptions,
|
||||
Class<?> entityType) {
|
||||
|
||||
CreateCollectionOptions result = new CreateCollectionOptions();
|
||||
|
||||
if (collectionOptions != null) {
|
||||
|
||||
collectionOptions.getCapped().ifPresent(result::capped);
|
||||
collectionOptions.getSize().ifPresent(result::sizeInBytes);
|
||||
collectionOptions.getMaxDocuments().ifPresent(result::maxDocuments);
|
||||
collectionOptions.getCollation().map(Collation::toMongoCollation).ifPresent(result::collation);
|
||||
if (collectionOptions == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
collectionOptions.getCapped().ifPresent(result::capped);
|
||||
collectionOptions.getSize().ifPresent(result::sizeInBytes);
|
||||
collectionOptions.getMaxDocuments().ifPresent(result::maxDocuments);
|
||||
collectionOptions.getCollation().map(Collation::toMongoCollation).ifPresent(result::collation);
|
||||
|
||||
collectionOptions.getValidator().ifPresent(it -> {
|
||||
|
||||
ValidationOptions validationOptions = new ValidationOptions();
|
||||
|
||||
it.getValidationAction().ifPresent(validationOptions::validationAction);
|
||||
it.getValidationLevel().ifPresent(validationOptions::validationLevel);
|
||||
|
||||
it.getSchema()
|
||||
.ifPresent(val -> validationOptions.validator(schemaMapper.mapSchema(val.toDocument(), entityType)));
|
||||
|
||||
result.validationOptions(validationOptions);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.util.Assert;
|
||||
* provided domain type.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public class MongoJsonSchemaMapper implements JsonSchemaMapper {
|
||||
@@ -60,7 +61,7 @@ public class MongoJsonSchemaMapper implements JsonSchemaMapper {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* (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) {
|
||||
@@ -78,7 +79,8 @@ public class MongoJsonSchemaMapper implements JsonSchemaMapper {
|
||||
mapSchemaObject(mappingContext.getPersistentEntity(type), jsonSchema.get($JSON_SCHEMA, Document.class)));
|
||||
}
|
||||
|
||||
private Document mapSchemaObject(@Nullable PersistentEntity entity, Document source) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private Document mapSchemaObject(@Nullable PersistentEntity<?, MongoPersistentProperty> entity, Document source) {
|
||||
|
||||
Document sink = new Document(source);
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
* Value object representing a MongoDB-specific JSON schema which is the default {@link MongoJsonSchema} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
class DefaultMongoJsonSchema implements MongoJsonSchema {
|
||||
|
||||
private final @NonNull JsonSchemaObject root;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.MongoJsonSchema#toDocument()
|
||||
*/
|
||||
@Override
|
||||
public Document toDocument() {
|
||||
return new Document("$jsonSchema", root.toDocument());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
* JSON schema backed by a {@link org.bson.Document} object.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
class DocumentJsonSchema implements MongoJsonSchema {
|
||||
|
||||
private final @NonNull Document document;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.MongoJsonSchema#toDocument()
|
||||
*/
|
||||
@Override
|
||||
public Document toDocument() {
|
||||
return new Document("$jsonSchema", new Document(document));
|
||||
}
|
||||
}
|
||||
@@ -32,16 +32,23 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link JsonSchemaProperty} implementation.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implements JsonSchemaProperty {
|
||||
|
||||
protected String identifier;
|
||||
protected T jsonSchemaObjectDelegate;
|
||||
protected final String identifier;
|
||||
protected final T jsonSchemaObjectDelegate;
|
||||
|
||||
public IdentifiableJsonSchemaProperty(String identifier, T jsonSchemaObject) {
|
||||
/**
|
||||
* Creates a new {@link IdentifiableJsonSchemaProperty} for {@code identifier} and {@code jsonSchemaObject}.
|
||||
*
|
||||
* @param identifier must not be {@literal null}.
|
||||
* @param jsonSchemaObject must not be {@literal null}.
|
||||
*/
|
||||
IdentifiableJsonSchemaProperty(String identifier, T jsonSchemaObject) {
|
||||
|
||||
Assert.notNull(identifier, "Identifier must not be null!");
|
||||
Assert.notNull(jsonSchemaObject, "JsonSchemaObject must not be null!");
|
||||
@@ -85,7 +92,7 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
*/
|
||||
public static class UntypedJsonSchemaProperty extends IdentifiableJsonSchemaProperty<UntypedJsonSchemaObject> {
|
||||
|
||||
public UntypedJsonSchemaProperty(String identifier, UntypedJsonSchemaObject jsonSchemaObject) {
|
||||
UntypedJsonSchemaProperty(String identifier, UntypedJsonSchemaObject jsonSchemaObject) {
|
||||
super(identifier, jsonSchemaObject);
|
||||
}
|
||||
|
||||
@@ -201,7 +208,7 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
* {@literal null} nor {@literal empty}.
|
||||
* @param schemaObject must not be {@literal null}.
|
||||
*/
|
||||
public StringJsonSchemaProperty(String identifier, StringJsonSchemaObject schemaObject) {
|
||||
StringJsonSchemaProperty(String identifier, StringJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
@@ -274,8 +281,7 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
* @see StringJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public StringJsonSchemaProperty possibleValues(Collection<String> possibleValues) {
|
||||
return new StringJsonSchemaProperty(identifier,
|
||||
jsonSchemaObjectDelegate.possibleValues((Collection) possibleValues));
|
||||
return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,35 +351,35 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
* {@literal null} nor {@literal empty}.
|
||||
* @param schemaObject must not be {@literal null}.
|
||||
*/
|
||||
public ObjectJsonSchemaProperty(String identifier, ObjectJsonSchemaObject schemaObject) {
|
||||
ObjectJsonSchemaProperty(String identifier, ObjectJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param range must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#nrProperties
|
||||
* @see ObjectJsonSchemaObject#propertiesCount
|
||||
*/
|
||||
public ObjectJsonSchemaProperty nrProperties(Range<Integer> range) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.nrProperties(range));
|
||||
public ObjectJsonSchemaProperty propertiesCount(Range<Integer> range) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.propertiesCount(range));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nrProperties must not be {@literal null}.
|
||||
* @param count must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#minNrProperties(int)
|
||||
* @see ObjectJsonSchemaObject#minProperties(int)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty minNrProperties(int nrProperties) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minNrProperties(nrProperties));
|
||||
public ObjectJsonSchemaProperty minProperties(int count) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minProperties(count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nrProperties must not be {@literal null}.
|
||||
* @param count must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaProperty}.
|
||||
* @see ObjectJsonSchemaObject#maxNrProperties(int)
|
||||
* @see ObjectJsonSchemaObject#maxProperties(int)
|
||||
*/
|
||||
public ObjectJsonSchemaProperty maxNrProperties(int nrProperties) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxNrProperties(nrProperties));
|
||||
public ObjectJsonSchemaProperty maxProperties(int count) {
|
||||
return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxProperties(count));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -626,8 +632,7 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
* @see NumericJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public NumericJsonSchemaProperty possibleValues(Collection<Number> possibleValues) {
|
||||
return new NumericJsonSchemaProperty(identifier,
|
||||
jsonSchemaObjectDelegate.possibleValues((Collection) possibleValues));
|
||||
return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -719,6 +724,33 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.range(range));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param count
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#minItems(int)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty minItems(int count) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minItems(count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param count
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#maxItems(int)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty maxItems(int count) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxItems(count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param items must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#items(Collection)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty items(JsonSchemaObject... items) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.items(Arrays.asList(items)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param items must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
@@ -728,6 +760,15 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.items(items));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param additionalItemsAllowed
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObject#additionalItems(boolean)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty additionalItems(boolean additionalItemsAllowed) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.additionalItems(additionalItemsAllowed));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link ArrayJsonSchemaProperty}.
|
||||
@@ -812,7 +853,7 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @see ArrayJsonSchemaObjecty#description(String)
|
||||
* @see ArrayJsonSchemaObject#description(String)
|
||||
*/
|
||||
public ArrayJsonSchemaProperty description(String description) {
|
||||
return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
@@ -835,7 +876,7 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
*/
|
||||
public static class BooleanJsonSchemaProperty extends IdentifiableJsonSchemaProperty<BooleanJsonSchemaObject> {
|
||||
|
||||
public BooleanJsonSchemaProperty(String identifier, BooleanJsonSchemaObject schemaObject) {
|
||||
BooleanJsonSchemaProperty(String identifier, BooleanJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
@@ -865,18 +906,25 @@ public class IdentifiableJsonSchemaProperty<T extends JsonSchemaObject> implemen
|
||||
*/
|
||||
public static class NullJsonSchemaProperty extends IdentifiableJsonSchemaProperty<NullJsonSchemaObject> {
|
||||
|
||||
public NullJsonSchemaProperty(String identifier, NullJsonSchemaObject schemaObject) {
|
||||
NullJsonSchemaProperty(String identifier, NullJsonSchemaObject schemaObject) {
|
||||
super(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaProperty}.
|
||||
* @return new instance of {@link NullJsonSchemaProperty}.
|
||||
* @see NullJsonSchemaObject#description(String)
|
||||
*/
|
||||
public NullJsonSchemaProperty description(String description) {
|
||||
return new NullJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new instance of {@link NullJsonSchemaProperty}.
|
||||
* @see NullJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
public NullJsonSchemaProperty generatedDescription() {
|
||||
return new NullJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bson.BsonTimestamp;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject;
|
||||
@@ -36,7 +37,18 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Interface that can be implemented by objects that know how to serialize themselves to JSON schema using
|
||||
* {@link #toDocument()}.
|
||||
* <p/>
|
||||
* This class also declares factory methods for type-specific {@link JsonSchemaObject schema objects} such as
|
||||
* {@link #string()} or {@link #object()}. For example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* JsonSchemaProperty.object("address").properties(JsonSchemaProperty.string("city").minLength(3));
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface JsonSchemaObject {
|
||||
@@ -134,7 +146,10 @@ public interface JsonSchemaObject {
|
||||
/**
|
||||
* Create a new {@link JsonSchemaObject} matching the given {@code type}.
|
||||
*
|
||||
* @param type Java class to create a {@link JsonSchemaObject} for. May be {@literal null} to create
|
||||
* {@link Type#nullType() null} type.
|
||||
* @return never {@literal null}.
|
||||
* @throws IllegalArgumentException if {@code type} is not supported.
|
||||
*/
|
||||
static TypedJsonSchemaObject of(@Nullable Class<?> type) {
|
||||
|
||||
@@ -167,6 +182,10 @@ public interface JsonSchemaObject {
|
||||
return of(Type.dateType());
|
||||
}
|
||||
|
||||
if (ClassUtils.isAssignable(BsonTimestamp.class, type)) {
|
||||
return of(Type.timestampType());
|
||||
}
|
||||
|
||||
if (ClassUtils.isAssignable(Pattern.class, type)) {
|
||||
return of(Type.regexType());
|
||||
}
|
||||
@@ -200,11 +219,11 @@ public interface JsonSchemaObject {
|
||||
return of(Type.numberType());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("No json schema type found for %s.", type));
|
||||
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}.
|
||||
* Type represents either a JSON schema {@literal type} or a MongoDB specific {@literal bsonType}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
@@ -212,29 +231,29 @@ public interface JsonSchemaObject {
|
||||
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");
|
||||
Type OBJECT_ID = bsonTypeOf("objectId");
|
||||
Type REGULAR_EXPRESSION = bsonTypeOf("regex");
|
||||
Type DOUBLE = bsonTypeOf("double");
|
||||
Type BINARY_DATA = bsonTypeOf("binData");
|
||||
Type DATE = bsonTypeOf("date");
|
||||
Type JAVA_SCRIPT = bsonTypeOf("javascript");
|
||||
Type INT_32 = bsonTypeOf("int");
|
||||
Type INT_64 = bsonTypeOf("long");
|
||||
Type DECIMAL_128 = bsonTypeOf("decimal");
|
||||
Type TIMESTAMP = bsonTypeOf("timestamp");
|
||||
|
||||
final Set<Type> BSON_TYPES = new HashSet<>(Arrays.asList(OBJECT_ID, REGULAR_EXPRESSION, DOUBLE, BINARY_DATA, DATE,
|
||||
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");
|
||||
Type OBJECT = jsonTypeOf("object");
|
||||
Type ARRAY = jsonTypeOf("array");
|
||||
Type NUMBER = jsonTypeOf("number");
|
||||
Type BOOLEAN = jsonTypeOf("boolean");
|
||||
Type STRING = jsonTypeOf("string");
|
||||
Type NULL = jsonTypeOf("null");
|
||||
|
||||
final Set<Type> JSON_TYPES = new HashSet<>(Arrays.asList(OBJECT, ARRAY, NUMBER, BOOLEAN, STRING, NULL));
|
||||
Set<Type> JSON_TYPES = new HashSet<>(Arrays.asList(OBJECT, ARRAY, NUMBER, BOOLEAN, STRING, NULL));
|
||||
|
||||
/**
|
||||
* @return a constant {@link Type} representing {@code bsonType : 'objectId' }.
|
||||
@@ -362,10 +381,16 @@ public interface JsonSchemaObject {
|
||||
return new JsonType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all known JSON types.
|
||||
*/
|
||||
static Set<Type> jsonTypes() {
|
||||
return JSON_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all known BSON types.
|
||||
*/
|
||||
static Set<Type> bsonTypes() {
|
||||
return BSON_TYPES;
|
||||
}
|
||||
@@ -389,15 +414,23 @@ public interface JsonSchemaObject {
|
||||
* @since 2.1
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class JsonType implements Type {
|
||||
class JsonType implements Type {
|
||||
|
||||
private final String name;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type#representation()
|
||||
*/
|
||||
@Override
|
||||
public String representation() {
|
||||
return "type";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type#value()
|
||||
*/
|
||||
@Override
|
||||
public String value() {
|
||||
return name;
|
||||
@@ -409,15 +442,23 @@ public interface JsonSchemaObject {
|
||||
* @since 2.1
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class BsonType implements Type {
|
||||
class BsonType implements Type {
|
||||
|
||||
private final String name;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type#representation()
|
||||
*/
|
||||
@Override
|
||||
public String representation() {
|
||||
return "bsonType";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type#value()
|
||||
*/
|
||||
@Override
|
||||
public String value() {
|
||||
return name;
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.schema;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
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;
|
||||
@@ -29,6 +32,7 @@ import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.Object
|
||||
* A {@literal property} or {@literal patternProperty} within a {@link JsonSchemaObject} of {@code type : 'object'}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface JsonSchemaProperty extends JsonSchemaObject {
|
||||
@@ -165,7 +169,7 @@ public interface JsonSchemaProperty extends JsonSchemaObject {
|
||||
|
||||
/**
|
||||
* Obtain a builder to create a {@link JsonSchemaProperty}.
|
||||
*
|
||||
*
|
||||
* @param identifier
|
||||
* @return
|
||||
*/
|
||||
@@ -173,24 +177,39 @@ public interface JsonSchemaProperty extends JsonSchemaObject {
|
||||
return new JsonSchemaPropertyBuilder(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link IdentifiableJsonSchemaProperty}.
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
class JsonSchemaPropertyBuilder {
|
||||
|
||||
private String identifier;
|
||||
|
||||
public JsonSchemaPropertyBuilder(String identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
private final String identifier;
|
||||
|
||||
/**
|
||||
* Configure a {@link Type} for the property.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public IdentifiableJsonSchemaProperty<TypedJsonSchemaObject> ofType(Type type) {
|
||||
return new IdentifiableJsonSchemaProperty(identifier, TypedJsonSchemaObject.of(type));
|
||||
return new IdentifiableJsonSchemaProperty<>(identifier, TypedJsonSchemaObject.of(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@link TypedJsonSchemaObject} for the property.
|
||||
*
|
||||
* @param schemaObject must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public IdentifiableJsonSchemaProperty<TypedJsonSchemaObject> with(TypedJsonSchemaObject schemaObject) {
|
||||
return new IdentifiableJsonSchemaProperty(identifier, schemaObject);
|
||||
return new IdentifiableJsonSchemaProperty<>(identifier, schemaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an untyped {@link IdentifiableJsonSchemaProperty}.
|
||||
*/
|
||||
public IdentifiableJsonSchemaProperty<UntypedJsonSchemaObject> withoutType() {
|
||||
return new IdentifiableJsonSchemaProperty(identifier, UntypedJsonSchemaObject.newInstance());
|
||||
return new IdentifiableJsonSchemaProperty<>(identifier, UntypedJsonSchemaObject.newInstance());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.Set;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Interface defining MongoDB-specific JSON schema object. New objects can be built with {@link #builder()}, for
|
||||
@@ -55,28 +54,21 @@ import org.springframework.util.Assert;
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @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;
|
||||
}
|
||||
* @see UntypedJsonSchemaObject
|
||||
* @see TypedJsonSchemaObject
|
||||
*/
|
||||
public interface MongoJsonSchema {
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* Property and field names need to be mapped to the domain type ones by running the {@link Document} through a
|
||||
* {@link org.springframework.data.mongodb.core.convert.JsonSchemaMapper} to apply field name customization.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public Document toDocument() {
|
||||
return new Document("$jsonSchema", root.toDocument());
|
||||
}
|
||||
Document toDocument();
|
||||
|
||||
/**
|
||||
* Create a new {@link MongoJsonSchema} for a given root object.
|
||||
@@ -84,8 +76,18 @@ public class MongoJsonSchema {
|
||||
* @param root must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static MongoJsonSchema of(JsonSchemaObject root) {
|
||||
return new MongoJsonSchema(root);
|
||||
static MongoJsonSchema of(JsonSchemaObject root) {
|
||||
return new DefaultMongoJsonSchema(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MongoJsonSchema} for a given root {@link Document} containing the schema definition.
|
||||
*
|
||||
* @param document must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
static MongoJsonSchema of(Document document) {
|
||||
return new DocumentJsonSchema(document);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,16 +95,16 @@ public class MongoJsonSchema {
|
||||
*
|
||||
* @return new instance of {@link MongoJsonSchemaBuilder}.
|
||||
*/
|
||||
public static MongoJsonSchemaBuilder builder() {
|
||||
static MongoJsonSchemaBuilder builder() {
|
||||
return new MongoJsonSchemaBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MongoJsonSchemaBuilder} provides a fluent API for defining a {@link MongoJsonSchema}.
|
||||
*
|
||||
* @since 2.1
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class MongoJsonSchemaBuilder {
|
||||
class MongoJsonSchemaBuilder {
|
||||
|
||||
private ObjectJsonSchemaObject root;
|
||||
|
||||
@@ -111,141 +113,155 @@ public class MongoJsonSchema {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nrProperties
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#minNrProperties(int)
|
||||
* @param count
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#minProperties(int)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder minNrProperties(int nrProperties) {
|
||||
root = root.minNrProperties(nrProperties);
|
||||
public MongoJsonSchemaBuilder minProperties(int count) {
|
||||
|
||||
root = root.minProperties(count);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nrProperties
|
||||
* @return this
|
||||
* @see ObjectJsonSchemaObject#maxNrProperties(int)
|
||||
* @param count
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#maxProperties(int)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder maxNrProperties(int nrProperties) {
|
||||
root = root.maxNrProperties(nrProperties);
|
||||
public MongoJsonSchemaBuilder maxProperties(int count) {
|
||||
|
||||
root = root.maxProperties(count);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties
|
||||
* @return
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#required(String...)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder required(String... properties) {
|
||||
|
||||
root = root.required(properties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param additionalPropertiesAllowed
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#additionalProperties(boolean)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder additionalProperties(boolean additionalPropertiesAllowed) {
|
||||
|
||||
root = root.additionalProperties(additionalPropertiesAllowed);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param schema
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#additionalProperties(ObjectJsonSchemaObject)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder additionalProperties(ObjectJsonSchemaObject schema) {
|
||||
|
||||
root = root.additionalProperties(schema);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#properties(JsonSchemaProperty...)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder properties(JsonSchemaProperty... properties) {
|
||||
|
||||
root = root.properties(properties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#patternProperties(JsonSchemaProperty...)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder patternProperties(JsonSchemaProperty... properties) {
|
||||
|
||||
root = root.patternProperties(properties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param property
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#property(JsonSchemaProperty)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder property(JsonSchemaProperty property) {
|
||||
|
||||
root = root.property(property);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param possibleValues
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see ObjectJsonSchemaObject#possibleValues(Collection)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder possibleValues(Set<Object> possibleValues) {
|
||||
|
||||
root = root.possibleValues(possibleValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allOf
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see UntypedJsonSchemaObject#allOf(Collection)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder allOf(Set<JsonSchemaObject> allOf) {
|
||||
|
||||
root = root.allOf(allOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anyOf
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see UntypedJsonSchemaObject#anyOf(Collection)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder anyOf(Set<JsonSchemaObject> anyOf) {
|
||||
|
||||
root = root.anyOf(anyOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oneOf
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see UntypedJsonSchemaObject#oneOf(Collection)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder oneOf(Set<JsonSchemaObject> oneOf) {
|
||||
|
||||
root = root.oneOf(oneOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notMatch
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see UntypedJsonSchemaObject#notMatch(JsonSchemaObject)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder notMatch(JsonSchemaObject notMatch) {
|
||||
|
||||
root = root.notMatch(notMatch);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description
|
||||
* @return this
|
||||
* @return {@code this} {@link MongoJsonSchemaBuilder}.
|
||||
* @see UntypedJsonSchemaObject#description(String)
|
||||
*/
|
||||
public MongoJsonSchemaBuilder description(String description) {
|
||||
|
||||
root = root.description(description);
|
||||
return this;
|
||||
}
|
||||
@@ -259,5 +275,4 @@ public class MongoJsonSchema {
|
||||
return MongoJsonSchema.of(root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.util.StringUtils;
|
||||
* A {@link JsonSchemaObject} of a given {@link org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
@@ -51,8 +52,9 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @param description can be {@literal null}.
|
||||
* @param restrictions can be {@literal null}.
|
||||
*/
|
||||
protected TypedJsonSchemaObject(@Nullable Type type, @Nullable String description, boolean generateDescription,
|
||||
TypedJsonSchemaObject(@Nullable Type type, @Nullable String description, boolean generateDescription,
|
||||
@Nullable Restrictions restrictions) {
|
||||
|
||||
this(type != null ? Collections.singleton(type) : Collections.emptySet(), description, generateDescription,
|
||||
restrictions);
|
||||
}
|
||||
@@ -62,10 +64,11 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @param description can be {@literal null}.
|
||||
* @param restrictions can be {@literal null}. Defaults to {@link Restrictions#empty()}.
|
||||
*/
|
||||
protected TypedJsonSchemaObject(Set<Type> types, @Nullable String description, boolean generateDescription,
|
||||
TypedJsonSchemaObject(Set<Type> types, @Nullable String description, boolean generateDescription,
|
||||
@Nullable Restrictions restrictions) {
|
||||
|
||||
super(restrictions, description, generateDescription);
|
||||
|
||||
Assert.notNull(types, "Types must not be null! Please consider using 'Collections.emptySet()'.");
|
||||
|
||||
this.types = types;
|
||||
@@ -73,16 +76,27 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
/**
|
||||
* Creates new {@link TypedJsonSchemaObject} of given types.
|
||||
*
|
||||
*
|
||||
* @param types must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static TypedJsonSchemaObject of(Type... types) {
|
||||
|
||||
Assert.notNull(types, "Types must not be null!");
|
||||
Assert.noNullElements(types, "Types must not contain null!");
|
||||
|
||||
return new TypedJsonSchemaObject(new LinkedHashSet<>(Arrays.asList(types)), null, false, Restrictions.empty());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject#getTypes()
|
||||
*/
|
||||
@Override
|
||||
public Set<Type> getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@literal description}.
|
||||
*
|
||||
@@ -112,7 +126,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
@Override
|
||||
public TypedJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
public TypedJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
|
||||
return new TypedJsonSchemaObject(types, description, generateDescription,
|
||||
restrictions.possibleValues(possibleValues));
|
||||
}
|
||||
@@ -161,17 +175,8 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.notMatch(notMatch));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject#getTypes()
|
||||
*/
|
||||
@Override
|
||||
public Set<Type> getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the json schema complying {@link Document} representation. This includes {@literal type},
|
||||
* Create the JSON schema complying {@link Document} representation. This includes {@literal type},
|
||||
* {@literal description} and the fields of {@link Restrictions#toDocument()} if set.
|
||||
*/
|
||||
@Override
|
||||
@@ -190,10 +195,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
}
|
||||
|
||||
getOrCreateDescription().ifPresent(val -> document.append("description", val));
|
||||
|
||||
if (restrictions != null) {
|
||||
document.putAll(restrictions.toDocument());
|
||||
}
|
||||
document.putAll(restrictions.toDocument());
|
||||
|
||||
return document;
|
||||
}
|
||||
@@ -226,9 +228,9 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
static class ObjectJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
public static class ObjectJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
|
||||
private @Nullable Range<Integer> nrProperties;
|
||||
private @Nullable Range<Integer> propertiesCount;
|
||||
private @Nullable Object additionalProperties;
|
||||
private List<String> requiredProperties = Collections.emptyList();
|
||||
private List<JsonSchemaProperty> properties = Collections.emptyList();
|
||||
@@ -254,35 +256,35 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @param range must not be {@literal null}. Consider {@link Range#unbounded()} instead.
|
||||
* @return new instance of {@link ObjectJsonSchemaObject}.
|
||||
*/
|
||||
public ObjectJsonSchemaObject nrProperties(Range<Integer> range) {
|
||||
public ObjectJsonSchemaObject propertiesCount(Range<Integer> range) {
|
||||
|
||||
ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.nrProperties = range;
|
||||
newInstance.propertiesCount = range;
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the {@literal minProperties}.
|
||||
*
|
||||
* @param nrProperties the allowed minimal number of properties.
|
||||
* @param count the allowed minimal number of properties.
|
||||
* @return new instance of {@link ObjectJsonSchemaObject}.
|
||||
*/
|
||||
public ObjectJsonSchemaObject minNrProperties(int nrProperties) {
|
||||
public ObjectJsonSchemaObject minProperties(int count) {
|
||||
|
||||
Bound upper = this.nrProperties != null ? this.nrProperties.getUpperBound() : Bound.unbounded();
|
||||
return nrProperties(Range.of(Bound.inclusive(nrProperties), upper));
|
||||
Bound<Integer> upper = this.propertiesCount != null ? this.propertiesCount.getUpperBound() : Bound.unbounded();
|
||||
return propertiesCount(Range.of(Bound.inclusive(count), upper));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the {@literal maxProperties}.
|
||||
*
|
||||
* @param nrProperties the allowed maximum number of properties.
|
||||
* @param count the allowed maximum number of properties.
|
||||
* @return new instance of {@link ObjectJsonSchemaObject}.
|
||||
*/
|
||||
public ObjectJsonSchemaObject maxNrProperties(int nrProperties) {
|
||||
public ObjectJsonSchemaObject maxProperties(int count) {
|
||||
|
||||
Bound lower = this.nrProperties != null ? this.nrProperties.getLowerBound() : Bound.unbounded();
|
||||
return nrProperties(Range.of(lower, Bound.inclusive(nrProperties)));
|
||||
Bound<Integer> lower = this.propertiesCount != null ? this.propertiesCount.getLowerBound() : Bound.unbounded();
|
||||
return propertiesCount(Range.of(lower, Bound.inclusive(count)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,11 +299,13 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
newInstance.requiredProperties = new ArrayList<>(this.requiredProperties.size() + properties.length);
|
||||
newInstance.requiredProperties.addAll(this.requiredProperties);
|
||||
newInstance.requiredProperties.addAll(Arrays.asList(properties));
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal false}, no additional fields are allowed.
|
||||
* If set to {@literal false}, additional fields besides
|
||||
* {@link #properties(JsonSchemaProperty...)}/{@link #patternProperties(JsonSchemaProperty...)} are not allowed.
|
||||
*
|
||||
* @param additionalPropertiesAllowed
|
||||
* @return new instance of {@link ObjectJsonSchemaObject}.
|
||||
@@ -310,6 +314,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.additionalProperties = additionalPropertiesAllowed;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@@ -338,6 +343,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
newInstance.properties = new ArrayList<>(this.properties.size() + properties.length);
|
||||
newInstance.properties.addAll(this.properties);
|
||||
newInstance.properties.addAll(Arrays.asList(properties));
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@@ -354,13 +360,14 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
newInstance.patternProperties = new ArrayList<>(this.patternProperties.size() + regularExpressions.length);
|
||||
newInstance.patternProperties.addAll(this.patternProperties);
|
||||
newInstance.patternProperties.addAll(Arrays.asList(regularExpressions));
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the objects property along with the {@link JsonSchemaObject} validating against.
|
||||
*
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param property must not be {@literal null}.
|
||||
* @return new instance of {@link ObjectJsonSchemaObject}.
|
||||
*/
|
||||
public ObjectJsonSchemaObject property(JsonSchemaProperty property) {
|
||||
@@ -372,7 +379,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @see org.springframework.data.mongodb.core.schema.UntypedJsonSchemaObject#possibleValues(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public ObjectJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
public ObjectJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
|
||||
return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
@@ -442,17 +449,12 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
doc.append("required", requiredProperties);
|
||||
}
|
||||
|
||||
if (nrProperties != null) {
|
||||
if (propertiesCount != null) {
|
||||
|
||||
if (nrProperties.getLowerBound().isBounded()) {
|
||||
doc.append("minProperties", nrProperties.getLowerBound().getValue().get());
|
||||
|
||||
}
|
||||
|
||||
if (nrProperties.getUpperBound().isBounded()) {
|
||||
doc.append("maxProperties", nrProperties.getUpperBound().getValue().get());
|
||||
}
|
||||
propertiesCount.getLowerBound().getValue().ifPresent(it -> doc.append("minProperties", it));
|
||||
propertiesCount.getUpperBound().getValue().ifPresent(it -> doc.append("maxProperties", it));
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(properties)) {
|
||||
doc.append("properties", reduceToDocument(properties));
|
||||
}
|
||||
@@ -469,15 +471,17 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
return doc;
|
||||
}
|
||||
|
||||
private ObjectJsonSchemaObject newInstance(String description, boolean generateDescription,
|
||||
private ObjectJsonSchemaObject newInstance(@Nullable String description, boolean generateDescription,
|
||||
Restrictions restrictions) {
|
||||
|
||||
ObjectJsonSchemaObject newInstance = new ObjectJsonSchemaObject(description, generateDescription, restrictions);
|
||||
|
||||
newInstance.properties = this.properties;
|
||||
newInstance.requiredProperties = this.requiredProperties;
|
||||
newInstance.additionalProperties = this.additionalProperties;
|
||||
newInstance.nrProperties = this.nrProperties;
|
||||
newInstance.propertiesCount = this.propertiesCount;
|
||||
newInstance.patternProperties = this.patternProperties;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@@ -485,43 +489,46 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
return source.stream() //
|
||||
.map(JsonSchemaProperty::toDocument) //
|
||||
.collect(Document::new, (target, propertyDocument) -> target.putAll(propertyDocument),
|
||||
(target, propertyDocument) -> {});
|
||||
.collect(Document::new, Document::putAll, (target, propertyDocument) -> {});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
@Override
|
||||
protected String generateDescription() {
|
||||
|
||||
String errorMsg = "Must be an object";
|
||||
String description = "Must be an object";
|
||||
|
||||
if (nrProperties != null) {
|
||||
errorMsg += " with " + nrProperties + " properties";
|
||||
if (propertiesCount != null) {
|
||||
description += String.format(" with %s properties", propertiesCount);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(requiredProperties)) {
|
||||
|
||||
if (requiredProperties.size() == 1) {
|
||||
errorMsg += " where " + requiredProperties.iterator().next() + "is mandatory";
|
||||
description += String.format(" where %sis mandatory", requiredProperties.iterator().next());
|
||||
} else {
|
||||
errorMsg += " where " + StringUtils.collectionToDelimitedString(requiredProperties, ", ") + " are mandatory";
|
||||
description += String.format(" where %s are mandatory",
|
||||
StringUtils.collectionToDelimitedString(requiredProperties, ", "));
|
||||
}
|
||||
}
|
||||
if (additionalProperties instanceof Boolean) {
|
||||
errorMsg += (((Boolean) additionalProperties) ? " " : " not ") + "allowing additional properties";
|
||||
description += (((Boolean) additionalProperties) ? " " : " not ") + "allowing additional properties";
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(properties)) {
|
||||
errorMsg += " defining restrictions for " + StringUtils.collectionToDelimitedString(
|
||||
properties.stream().map(val -> val.getIdentifier()).collect(Collectors.toList()), ", ");
|
||||
description += String.format(" defining restrictions for %s", StringUtils.collectionToDelimitedString(
|
||||
properties.stream().map(JsonSchemaProperty::getIdentifier).collect(Collectors.toList()), ", "));
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(patternProperties)) {
|
||||
errorMsg += " defining restrictions for patterns " + StringUtils.collectionToDelimitedString(
|
||||
patternProperties.stream().map(val -> val.getIdentifier()).collect(Collectors.toList()), ", ");
|
||||
description += String.format(" defining restrictions for patterns %s", StringUtils.collectionToDelimitedString(
|
||||
patternProperties.stream().map(JsonSchemaProperty::getIdentifier).collect(Collectors.toList()), ", "));
|
||||
}
|
||||
|
||||
return errorMsg + ".";
|
||||
return description + ".";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +541,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
static class NumericJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
public static class NumericJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
|
||||
private static final Set<Type> NUMERIC_TYPES = new HashSet<>(
|
||||
Arrays.asList(Type.doubleType(), Type.intType(), Type.longType(), Type.numberType(), Type.bigDecimalType()));
|
||||
@@ -542,11 +549,11 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
@Nullable Number multipleOf;
|
||||
@Nullable Range<? extends Number> range;
|
||||
|
||||
public NumericJsonSchemaObject() {
|
||||
NumericJsonSchemaObject() {
|
||||
this(Type.numberType());
|
||||
}
|
||||
|
||||
public NumericJsonSchemaObject(Type type) {
|
||||
NumericJsonSchemaObject(Type type) {
|
||||
this(type, null, false);
|
||||
}
|
||||
|
||||
@@ -571,6 +578,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
NumericJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.multipleOf = value;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@@ -587,6 +595,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
NumericJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.range = range;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@@ -596,6 +605,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @param min must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaObject}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public NumericJsonSchemaObject gt(Number min) {
|
||||
|
||||
Assert.notNull(min, "Min must not be null!");
|
||||
@@ -610,6 +620,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @param min must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaObject}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public NumericJsonSchemaObject gte(Number min) {
|
||||
|
||||
Assert.notNull(min, "Min must not be null!");
|
||||
@@ -624,6 +635,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @param max must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaObject}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public NumericJsonSchemaObject lt(Number max) {
|
||||
|
||||
Assert.notNull(max, "Max must not be null!");
|
||||
@@ -638,6 +650,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @param max must not be {@literal null}.
|
||||
* @return new instance of {@link NumericJsonSchemaObject}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
NumericJsonSchemaObject lte(Number max) {
|
||||
|
||||
Assert.notNull(max, "Max must not be null!");
|
||||
@@ -651,7 +664,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#possibleValues(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public NumericJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
public NumericJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
|
||||
return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
@@ -706,7 +719,6 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
*/
|
||||
@Override
|
||||
public NumericJsonSchemaObject generatedDescription() {
|
||||
|
||||
return newInstance(description, true, restrictions);
|
||||
}
|
||||
|
||||
@@ -726,14 +738,16 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
if (range != null) {
|
||||
|
||||
if (range.getLowerBound().isBounded()) {
|
||||
doc.append("minimum", range.getLowerBound().getValue().get());
|
||||
|
||||
range.getLowerBound().getValue().ifPresent(it -> doc.append("minimum", it));
|
||||
if (!range.getLowerBound().isInclusive()) {
|
||||
doc.append("exclusiveMinimum", true);
|
||||
}
|
||||
}
|
||||
|
||||
if (range.getUpperBound().isBounded()) {
|
||||
doc.append("maximum", range.getUpperBound().getValue().get());
|
||||
|
||||
range.getUpperBound().getValue().ifPresent(it -> doc.append("maximum", it));
|
||||
if (!range.getUpperBound().isInclusive()) {
|
||||
doc.append("exclusiveMaximum", true);
|
||||
}
|
||||
@@ -743,7 +757,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
return doc;
|
||||
}
|
||||
|
||||
private NumericJsonSchemaObject newInstance(String description, boolean generateDescription,
|
||||
private NumericJsonSchemaObject newInstance(@Nullable String description, boolean generateDescription,
|
||||
Restrictions restrictions) {
|
||||
|
||||
NumericJsonSchemaObject newInstance = new NumericJsonSchemaObject(types, description, generateDescription,
|
||||
@@ -751,11 +765,12 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
newInstance.multipleOf = this.multipleOf;
|
||||
newInstance.range = this.range;
|
||||
|
||||
return newInstance;
|
||||
|
||||
}
|
||||
|
||||
private Bound createBound(Number number, boolean inclusive) {
|
||||
private static Bound<?> createBound(Number number, boolean inclusive) {
|
||||
|
||||
if (number instanceof Long) {
|
||||
return inclusive ? Bound.inclusive((Long) number) : Bound.exclusive((Long) number);
|
||||
@@ -786,20 +801,23 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
return types;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
@Override
|
||||
protected String generateDescription() {
|
||||
|
||||
String errorMsg = "Must be a numeric value";
|
||||
String description = "Must be a numeric value";
|
||||
|
||||
if (multipleOf != null) {
|
||||
errorMsg += " multiple of " + multipleOf;
|
||||
description += String.format(" multiple of %s", multipleOf);
|
||||
}
|
||||
if (range != null) {
|
||||
errorMsg += " within range " + range;
|
||||
description += String.format(" within range %s", range);
|
||||
}
|
||||
|
||||
return errorMsg + ".";
|
||||
return description + ".";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -811,12 +829,12 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
static class StringJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
public static class StringJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
|
||||
@Nullable Range<Integer> length;
|
||||
@Nullable String pattern;
|
||||
|
||||
public StringJsonSchemaObject() {
|
||||
StringJsonSchemaObject() {
|
||||
this(null, false, null);
|
||||
}
|
||||
|
||||
@@ -837,30 +855,31 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
StringJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.length = range;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the valid length range ({@literal minLength} for a valid field.
|
||||
* Define the valid length range ({@literal minLength}) for a valid field.
|
||||
*
|
||||
* @param length
|
||||
* @return new instance of {@link StringJsonSchemaObject}.
|
||||
*/
|
||||
public StringJsonSchemaObject minLength(int length) {
|
||||
|
||||
Bound upper = this.length != null ? this.length.getUpperBound() : Bound.unbounded();
|
||||
Bound<Integer> upper = this.length != null ? this.length.getUpperBound() : Bound.unbounded();
|
||||
return length(Range.of(Bound.inclusive(length), upper));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the valid length range ({@literal maxLength} for a valid field.
|
||||
* Define the valid length range ({@literal maxLength}) for a valid field.
|
||||
*
|
||||
* @param length
|
||||
* @return new instance of {@link StringJsonSchemaObject}.
|
||||
*/
|
||||
public StringJsonSchemaObject maxLength(int length) {
|
||||
|
||||
Bound lower = this.length != null ? this.length.getLowerBound() : Bound.unbounded();
|
||||
Bound<Integer> lower = this.length != null ? this.length.getLowerBound() : Bound.unbounded();
|
||||
return length(Range.of(lower, Bound.inclusive(length)));
|
||||
}
|
||||
|
||||
@@ -876,6 +895,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
StringJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.pattern = pattern;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@@ -884,7 +904,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#possibleValues(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public StringJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
public StringJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
|
||||
return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
@@ -953,13 +973,8 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
if (length != null) {
|
||||
|
||||
if (length.getLowerBound().isBounded()) {
|
||||
doc.append("minLength", length.getLowerBound().getValue().get());
|
||||
}
|
||||
|
||||
if (length.getUpperBound().isBounded()) {
|
||||
doc.append("maxLength", length.getUpperBound().getValue().get());
|
||||
}
|
||||
length.getLowerBound().getValue().ifPresent(it -> doc.append("minLength", it));
|
||||
length.getUpperBound().getValue().ifPresent(it -> doc.append("maxLength", it));
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(pattern)) {
|
||||
@@ -969,40 +984,54 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
return doc;
|
||||
}
|
||||
|
||||
private StringJsonSchemaObject newInstance(String description, boolean generateDescription,
|
||||
private StringJsonSchemaObject newInstance(@Nullable String description, boolean generateDescription,
|
||||
Restrictions restrictions) {
|
||||
|
||||
StringJsonSchemaObject newInstance = new StringJsonSchemaObject(description, generateDescription, restrictions);
|
||||
|
||||
newInstance.length = this.length;
|
||||
newInstance.pattern = this.pattern;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
@Override
|
||||
protected String generateDescription() {
|
||||
|
||||
String errorMsg = "Must be a string";
|
||||
String description = "Must be a string";
|
||||
|
||||
if (length != null) {
|
||||
errorMsg += " with length " + length;
|
||||
description += String.format(" with length %s", length);
|
||||
}
|
||||
if (pattern != null) {
|
||||
errorMsg += " matching " + pattern;
|
||||
description += String.format(" matching %s", pattern);
|
||||
}
|
||||
|
||||
return errorMsg + ".";
|
||||
return description + ".";
|
||||
}
|
||||
}
|
||||
|
||||
static class ArrayJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
/**
|
||||
* {@link JsonSchemaObject} implementation of {@code type : 'array'} schema elements.<br />
|
||||
* Provides programmatic access to schema specifics like {@literal range, minItems, maxItems,...} via a fluent API
|
||||
* producing immutable {@link JsonSchemaObject schema objects}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class ArrayJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
|
||||
private @Nullable Boolean uniqueItems;
|
||||
private @Nullable Boolean additionalItems;
|
||||
private @Nullable Range<Integer> range;
|
||||
private Collection<JsonSchemaObject> items = Collections.emptyList();
|
||||
|
||||
public ArrayJsonSchemaObject() {
|
||||
ArrayJsonSchemaObject() {
|
||||
this(null, false, null);
|
||||
}
|
||||
|
||||
@@ -1011,64 +1040,136 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
super(Collections.singleton(Type.arrayType()), description, generateDescription, restrictions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the whether the array must contain unique items.
|
||||
*
|
||||
* @param uniqueItems
|
||||
* @return new instance of {@link ArrayJsonSchemaObject}.
|
||||
*/
|
||||
public ArrayJsonSchemaObject uniqueItems(boolean uniqueItems) {
|
||||
|
||||
ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.uniqueItems = uniqueItems;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the {@literal minItems} and {@literal maxItems} via the given {@link Range}.<br />
|
||||
* In-/Exclusions via {@link Bound#isInclusive() range bounds} are not taken into account.
|
||||
*
|
||||
* @param range must not be {@literal null}. Consider {@link Range#unbounded()} instead.
|
||||
* @return new instance of {@link ArrayJsonSchemaObject}.
|
||||
*/
|
||||
public ArrayJsonSchemaObject range(Range<Integer> range) {
|
||||
|
||||
ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.range = range;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
public ArrayJsonSchemaObject maxItems(int nrItems) {
|
||||
/**
|
||||
* Define the {@literal maxItems}.
|
||||
*
|
||||
* @param count the allowed minimal number of array items.
|
||||
* @return new instance of {@link ArrayJsonSchemaObject}.
|
||||
*/
|
||||
public ArrayJsonSchemaObject minItems(int count) {
|
||||
|
||||
Bound lower = this.range != null ? this.range.getLowerBound() : Bound.unbounded();
|
||||
return range(Range.of(lower, Bound.inclusive(nrItems)));
|
||||
Bound<Integer> upper = this.range != null ? this.range.getUpperBound() : Bound.unbounded();
|
||||
return range(Range.of(Bound.inclusive(count), upper));
|
||||
}
|
||||
|
||||
public ArrayJsonSchemaObject minItems(int nrItems) {
|
||||
/**
|
||||
* Define the {@literal maxItems}.
|
||||
*
|
||||
* @param count the allowed maximal number of array items.
|
||||
* @return new instance of {@link ArrayJsonSchemaObject}.
|
||||
*/
|
||||
public ArrayJsonSchemaObject maxItems(int count) {
|
||||
|
||||
Bound upper = this.range != null ? this.range.getUpperBound() : Bound.unbounded();
|
||||
return range(Range.of(Bound.inclusive(nrItems), upper));
|
||||
Bound<Integer> lower = this.range != null ? this.range.getLowerBound() : Bound.unbounded();
|
||||
return range(Range.of(lower, Bound.inclusive(count)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the {@code items} allowed in the array.
|
||||
*
|
||||
* @param items the allowed items in the array.
|
||||
* @return new instance of {@link ArrayJsonSchemaObject}.
|
||||
*/
|
||||
public ArrayJsonSchemaObject items(Collection<JsonSchemaObject> items) {
|
||||
|
||||
ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.items = new ArrayList<>(items);
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal false}, no additional items besides {@link #items(Collection)} are allowed.
|
||||
*
|
||||
* @param additionalItemsAllowed {@literal true} to allow additional items in the array, {@literal false} otherwise.
|
||||
* @return new instance of {@link ArrayJsonSchemaObject}.
|
||||
*/
|
||||
public ArrayJsonSchemaObject additionalItems(boolean additionalItemsAllowed) {
|
||||
|
||||
ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
|
||||
newInstance.additionalItems = additionalItemsAllowed;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#possibleValues(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public ArrayJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
public ArrayJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
|
||||
return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#allOf(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public ArrayJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
|
||||
return newInstance(description, generateDescription, restrictions.allOf(allOf));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#anyOf(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public ArrayJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
|
||||
return newInstance(description, generateDescription, restrictions.anyOf(anyOf));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#oneOf(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public ArrayJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
|
||||
return newInstance(description, generateDescription, restrictions.oneOf(oneOf));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#notMatch(org.springframework.data.mongodb.core.schema.JsonSchemaObject)
|
||||
*/
|
||||
@Override
|
||||
public ArrayJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
|
||||
return newInstance(description, generateDescription, restrictions.notMatch(notMatch));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#description(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ArrayJsonSchemaObject description(String description) {
|
||||
return newInstance(description, generateDescription, restrictions);
|
||||
@@ -1083,8 +1184,13 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
return newInstance(description, true, restrictions);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#toDocument()
|
||||
*/
|
||||
@Override
|
||||
public Document toDocument() {
|
||||
|
||||
Document doc = new Document(super.toDocument());
|
||||
|
||||
if (!CollectionUtils.isEmpty(items)) {
|
||||
@@ -1094,59 +1200,80 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
|
||||
if (range != null) {
|
||||
|
||||
if (range.getLowerBound().isBounded()) {
|
||||
doc.append("minItems", range.getLowerBound().getValue().get());
|
||||
}
|
||||
|
||||
if (range.getUpperBound().isBounded()) {
|
||||
doc.append("maxItems", range.getUpperBound().getValue().get());
|
||||
}
|
||||
range.getLowerBound().getValue().ifPresent(it -> doc.append("minItems", it));
|
||||
range.getUpperBound().getValue().ifPresent(it -> doc.append("maxItems", it));
|
||||
}
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(uniqueItems, Boolean.TRUE)) {
|
||||
doc.append("uniqueItems", true);
|
||||
}
|
||||
|
||||
if (additionalItems != null) {
|
||||
doc.append("additionalItems", additionalItems);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private ArrayJsonSchemaObject newInstance(String description, boolean generateDescription,
|
||||
private ArrayJsonSchemaObject newInstance(@Nullable String description, boolean generateDescription,
|
||||
Restrictions restrictions) {
|
||||
|
||||
ArrayJsonSchemaObject newInstance = new ArrayJsonSchemaObject(description, generateDescription, restrictions);
|
||||
|
||||
newInstance.uniqueItems = this.uniqueItems;
|
||||
newInstance.range = this.range;
|
||||
newInstance.items = this.items;
|
||||
newInstance.additionalItems = this.additionalItems;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
@Override
|
||||
protected String generateDescription() {
|
||||
|
||||
String errorMsg = "Must be an array";
|
||||
String description = "Must be an array";
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(uniqueItems, Boolean.TRUE)) {
|
||||
errorMsg += " of unique values";
|
||||
description += " of unique values";
|
||||
}
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(additionalItems, Boolean.TRUE)) {
|
||||
description += " with additional items";
|
||||
}
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(additionalItems, Boolean.FALSE)) {
|
||||
description += " with no additional items";
|
||||
}
|
||||
|
||||
if (range != null) {
|
||||
errorMsg += " having size " + range;
|
||||
description += String.format(" having size %s", range);
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(items)) {
|
||||
errorMsg += " with items " + StringUtils.collectionToDelimitedString(
|
||||
items.stream().map(val -> val.toDocument()).collect(Collectors.toList()), ", ");
|
||||
description += String.format(" with items %s", StringUtils.collectionToDelimitedString(
|
||||
items.stream().map(JsonSchemaObject::toDocument).collect(Collectors.toList()), ", "));
|
||||
}
|
||||
|
||||
return errorMsg + ".";
|
||||
|
||||
return description + ".";
|
||||
}
|
||||
}
|
||||
|
||||
static class BooleanJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
/**
|
||||
* {@link JsonSchemaObject} implementation of {@code type : 'boolean'} schema elements.<br />
|
||||
* Provides programmatic access to schema specifics via a fluent API producing immutable {@link JsonSchemaObject
|
||||
* schema objects}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public static class BooleanJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
|
||||
public BooleanJsonSchemaObject() {
|
||||
BooleanJsonSchemaObject() {
|
||||
this(null, false, null);
|
||||
}
|
||||
|
||||
@@ -1160,7 +1287,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#possibleValues(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public BooleanJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
public BooleanJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
|
||||
return new BooleanJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
@@ -1218,11 +1345,28 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
return new BooleanJsonSchemaObject(description, true, restrictions);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
@Override
|
||||
protected String generateDescription() {
|
||||
return "Must be a boolean.";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link JsonSchemaObject} implementation of {@code type : 'null'} schema elements.<br />
|
||||
* Provides programmatic access to schema specifics via a fluent API producing immutable {@link JsonSchemaObject
|
||||
* schema objects}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
static class NullJsonSchemaObject extends TypedJsonSchemaObject {
|
||||
|
||||
public NullJsonSchemaObject() {
|
||||
NullJsonSchemaObject() {
|
||||
this(null, false, null);
|
||||
}
|
||||
|
||||
@@ -1236,7 +1380,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#possibleValues(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public NullJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
public NullJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
|
||||
return new NullJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues));
|
||||
}
|
||||
|
||||
@@ -1284,5 +1428,23 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject {
|
||||
public NullJsonSchemaObject description(String description) {
|
||||
return new NullJsonSchemaObject(description, generateDescription, restrictions);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#generatedDescription()
|
||||
*/
|
||||
@Override
|
||||
public NullJsonSchemaObject generatedDescription() {
|
||||
return new NullJsonSchemaObject(description, true, restrictions);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject#generateDescription()
|
||||
*/
|
||||
@Override
|
||||
protected String generateDescription() {
|
||||
return "Must be null.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.schema;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -28,27 +32,39 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Common base for {@link JsonSchemaObject} with shared types and {@link JsonSchemaObject#toDocument()} implementation.
|
||||
* Schema objects are immutable. Calling methods to configure properties creates a new object instance.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
|
||||
protected final @Nullable String description;
|
||||
protected final Restrictions restrictions;
|
||||
protected final @Nullable String description;
|
||||
protected final boolean generateDescription;
|
||||
|
||||
protected UntypedJsonSchemaObject(Restrictions restrictions, @Nullable String description, boolean generateDescription) {
|
||||
UntypedJsonSchemaObject(@Nullable Restrictions restrictions, @Nullable String description,
|
||||
boolean generateDescription) {
|
||||
|
||||
this.description = description;
|
||||
this.restrictions = restrictions != null ? restrictions : Restrictions.empty();
|
||||
this.generateDescription = generateDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link UntypedJsonSchemaObject}.
|
||||
*
|
||||
* @return the new {@link UntypedJsonSchemaObject}.
|
||||
*/
|
||||
public static UntypedJsonSchemaObject newInstance() {
|
||||
return new UntypedJsonSchemaObject(null, null, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.schema.JsonSchemaObject#getTypes()
|
||||
*/
|
||||
@Override
|
||||
public Set<Type> getTypes() {
|
||||
return Collections.emptySet();
|
||||
@@ -67,7 +83,6 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
/**
|
||||
* 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() {
|
||||
@@ -80,7 +95,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return new instance of {@link TypedJsonSchemaObject}.
|
||||
*/
|
||||
public UntypedJsonSchemaObject possibleValues(Collection<Object> possibleValues) {
|
||||
public UntypedJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
|
||||
return new UntypedJsonSchemaObject(restrictions.possibleValues(possibleValues), description, generateDescription);
|
||||
}
|
||||
|
||||
@@ -125,7 +140,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the json schema complying {@link Document} representation. This includes {@literal type},
|
||||
* Create the JSON schema complying {@link Document} representation. This includes {@literal type},
|
||||
* {@literal description} and the fields of {@link Restrictions#toDocument()} if set.
|
||||
*/
|
||||
@Override
|
||||
@@ -135,9 +150,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
|
||||
getOrCreateDescription().ifPresent(val -> document.append("description", val));
|
||||
|
||||
if (restrictions != null) {
|
||||
document.putAll(restrictions.toDocument());
|
||||
}
|
||||
document.putAll(restrictions.toDocument());
|
||||
|
||||
return document;
|
||||
}
|
||||
@@ -163,29 +176,21 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Restrictions} encapsulate common json schema restrictions like {@literal enum}, {@literal allOf}, ...
|
||||
* {@link Restrictions} encapsulates common JSON schema restrictions like {@literal enum}, {@literal allOf}, … that
|
||||
* are not tied to a specific type.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
static class Restrictions {
|
||||
|
||||
private final Collection<Object> possibleValues;
|
||||
private final Collection<? extends 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}.
|
||||
*/
|
||||
@@ -199,7 +204,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
* @param possibleValues must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Restrictions possibleValues(Collection<Object> possibleValues) {
|
||||
Restrictions possibleValues(Collection<? extends Object> possibleValues) {
|
||||
|
||||
Assert.notNull(possibleValues, "PossibleValues must not be null!");
|
||||
return new Restrictions(possibleValues, allOf, anyOf, oneOf, notMatch);
|
||||
@@ -246,7 +251,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the json schema complying {@link Document} representation. This includes {@literal enum},
|
||||
* 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}
|
||||
@@ -258,20 +263,28 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject {
|
||||
if (!CollectionUtils.isEmpty(possibleValues)) {
|
||||
document.append("enum", possibleValues);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(allOf)) {
|
||||
document.append("allOf", allOf.stream().map(JsonSchemaObject::toDocument).collect(Collectors.toList()));
|
||||
document.append("allOf", render(allOf));
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(anyOf)) {
|
||||
document.append("anyOf", anyOf.stream().map(JsonSchemaObject::toDocument).collect(Collectors.toList()));
|
||||
document.append("anyOf", render(anyOf));
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(oneOf)) {
|
||||
document.append("oneOf", oneOf.stream().map(JsonSchemaObject::toDocument).collect(Collectors.toList()));
|
||||
document.append("oneOf", render(oneOf));
|
||||
}
|
||||
|
||||
if (notMatch != null) {
|
||||
document.append("not", notMatch.toDocument());
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private static List<Document> render(Collection<JsonSchemaObject> objects) {
|
||||
return objects.stream().map(JsonSchemaObject::toDocument).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
/**
|
||||
* MongoDB-specific JSON schema implementation classes.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.data.domain.Range.*;
|
||||
* Tests verifying {@link org.bson.Document} representation of {@link JsonSchemaObject}s.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class JsonSchemaObjectUnitTests {
|
||||
|
||||
@@ -49,7 +50,7 @@ public class JsonSchemaObjectUnitTests {
|
||||
@Test // DATAMONGO-1835
|
||||
public void objectObjectShouldRenderNrPropertiesCorrectly() {
|
||||
|
||||
assertThat(object().nrProperties(from(inclusive(10)).to(inclusive(20))).generatedDescription().toDocument())
|
||||
assertThat(object().propertiesCount(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));
|
||||
}
|
||||
@@ -100,6 +101,7 @@ public class JsonSchemaObjectUnitTests {
|
||||
.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())
|
||||
@@ -228,6 +230,39 @@ public class JsonSchemaObjectUnitTests {
|
||||
.append("description", "Must be an array of unique values.").append("uniqueItems", true));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void arrayObjectShouldRenderAdditionalItemsItemsCorrectly() {
|
||||
|
||||
assertThat(array().additionalItems(true).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "array").append("description", "Must be an array with additional items.")
|
||||
.append("additionalItems", true));
|
||||
assertThat(array().additionalItems(false).generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "array").append("description", "Must be an array with no additional items.")
|
||||
.append("additionalItems", false));
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// type : 'boolean'
|
||||
// -----------------
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void booleanShouldRenderCorrectly() {
|
||||
|
||||
assertThat(bool().generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "boolean").append("description", "Must be a boolean."));
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// type : 'null'
|
||||
// -----------------
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void nullShouldRenderCorrectly() {
|
||||
|
||||
assertThat(nil().generatedDescription().toDocument())
|
||||
.isEqualTo(new Document("type", "null").append("description", "Must be null."));
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// type : 'any'
|
||||
// -----------------
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 org.junit.Test;
|
||||
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link JsonSchemaProperty}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class JsonSchemaPropertyUnitTests {
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void shouldRenderInt32Correctly() {
|
||||
assertThat(JsonSchemaProperty.int32("foo").toDocument()).containsEntry("foo.bsonType", "int");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void shouldRenderInt64Correctly() {
|
||||
assertThat(JsonSchemaProperty.int64("foo").toDocument()).containsEntry("foo.bsonType", "long");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void shouldRenderDecimal128Correctly() {
|
||||
assertThat(JsonSchemaProperty.decimal128("foo").toDocument()).containsEntry("foo.bsonType", "decimal");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void shouldRenderNullCorrectly() {
|
||||
assertThat(JsonSchemaProperty.nil("foo").toDocument()).containsEntry("foo.type", "null");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void shouldRenderUntypedCorrectly() {
|
||||
assertThat(JsonSchemaProperty.named("foo").ofType(Type.binaryType()).toDocument()).containsEntry("foo.bsonType",
|
||||
"binData");
|
||||
}
|
||||
}
|
||||
@@ -47,8 +47,9 @@ import com.mongodb.client.model.ValidationLevel;
|
||||
import com.mongodb.client.model.ValidationOptions;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoJsonSchema}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2017/12
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@@ -153,10 +154,6 @@ public class MongoJsonSchemaTests {
|
||||
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();
|
||||
@@ -186,5 +183,4 @@ public class MongoJsonSchemaTests {
|
||||
|
||||
@Field("post_code") String postCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,10 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MongoJsonSchema}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MongoJsonSchemaUnitTests {
|
||||
@@ -41,8 +44,26 @@ public class MongoJsonSchemaUnitTests {
|
||||
new Document("type", "object").append("required", Arrays.asList("firstname", "lastname"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1835
|
||||
public void rendersDocumentBasedSchemaCorrectly() {
|
||||
|
||||
Document document = MongoJsonSchema.builder() //
|
||||
.required("firstname", "lastname") //
|
||||
.build().toDocument();
|
||||
|
||||
MongoJsonSchema jsonSchema = MongoJsonSchema.of(document.get("$jsonSchema", Document.class));
|
||||
|
||||
assertThat(jsonSchema.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);
|
||||
MongoJsonSchema.of((JsonSchemaObject) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-1835
|
||||
public void throwsExceptionOnNullDocument() {
|
||||
MongoJsonSchema.of((Document) null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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 reactor.test.StepVerifier;
|
||||
|
||||
import java.time.Duration;
|
||||
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.AbstractReactiveMongoConfiguration;
|
||||
import org.springframework.data.mongodb.core.CollectionOptions;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||
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.reactivestreams.client.MongoClient;
|
||||
import com.mongodb.reactivestreams.client.MongoClients;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoJsonSchema} using reactive infrastructure.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class ReactiveMongoJsonSchemaTests {
|
||||
|
||||
public static @ClassRule MongoVersionRule REQUIRES_AT_LEAST_3_6_0 = MongoVersionRule.atLeast(Version.parse("3.6.0"));
|
||||
|
||||
@Configuration
|
||||
static class Config extends AbstractReactiveMongoConfiguration {
|
||||
|
||||
@Override
|
||||
public MongoClient reactiveMongoClient() {
|
||||
return MongoClients.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDatabaseName() {
|
||||
return "json-schema-tests";
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired ReactiveMongoTemplate template;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
StepVerifier.create(template.dropCollection(Person.class)).verifyComplete();
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
StepVerifier.create(template.createCollection(Person.class, CollectionOptions.empty().schema(schema)))
|
||||
.expectNextCount(1).verifyComplete();
|
||||
|
||||
Document $jsonSchema = new MongoJsonSchemaMapper(template.getConverter()).mapSchema(schema.toDocument(),
|
||||
Person.class);
|
||||
|
||||
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)))
|
||||
.block(Duration.ofSeconds(5));
|
||||
|
||||
if (collectionInfo == null) {
|
||||
throw new DataRetrievalFailureException(String.format("Collection %s was not found.", collectionName));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user