Polishing.
Fix Javadoc, make QueryCharacteristics a Streamable for easier usage during mapping, reformat code for reduced nesting levels. See #4988 Original pull request: #4992
This commit is contained in:
@@ -24,13 +24,13 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.bson.BsonBinary;
|
||||
import org.bson.BsonBinarySubType;
|
||||
import org.bson.BsonNull;
|
||||
import org.bson.Document;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty;
|
||||
@@ -391,6 +391,7 @@ public class CollectionOptions {
|
||||
*
|
||||
* @param encryptedFieldsOptions must not be {@literal null}.
|
||||
* @return new instance of {@link CollectionOptions}.
|
||||
* @since 4.5
|
||||
*/
|
||||
@Contract("_ -> new")
|
||||
@CheckReturnValue
|
||||
@@ -711,7 +712,7 @@ public class CollectionOptions {
|
||||
/**
|
||||
* @return new instance of {@link EncryptedFieldsOptions}.
|
||||
*/
|
||||
public static EncryptedFieldsOptions fromProperties(List<JsonSchemaProperty> properties) {
|
||||
public static EncryptedFieldsOptions fromProperties(List<? extends JsonSchemaProperty> properties) {
|
||||
return new EncryptedFieldsOptions(null, List.copyOf(properties));
|
||||
}
|
||||
|
||||
@@ -743,6 +744,7 @@ public class CollectionOptions {
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return new instance of {@link EncryptedFieldsOptions}.
|
||||
* @since 4.5.1
|
||||
*/
|
||||
@Contract("_ -> new")
|
||||
@CheckReturnValue
|
||||
@@ -751,24 +753,26 @@ public class CollectionOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link JsonSchemaProperty property} that should not be encrypted but not queryable.
|
||||
* Add a {@link JsonSchemaProperty property} that should be encrypted but not queryable.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @param key can be {@literal null}.
|
||||
* @param keyId the key identifier to be used, can be {@literal null}.
|
||||
* @return new instance of {@link EncryptedFieldsOptions}.
|
||||
* @since 4.5.1
|
||||
*/
|
||||
@Contract("_, _ -> new")
|
||||
@CheckReturnValue
|
||||
public EncryptedFieldsOptions encrypted(JsonSchemaProperty property, @Nullable Object key) {
|
||||
public EncryptedFieldsOptions encrypted(JsonSchemaProperty property, @Nullable Object keyId) {
|
||||
|
||||
List<JsonSchemaProperty> targetPropertyList = new ArrayList<>(properties.size() + 1);
|
||||
targetPropertyList.addAll(properties);
|
||||
|
||||
if (property instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty) {
|
||||
targetPropertyList.add(property);
|
||||
} else {
|
||||
EncryptedJsonSchemaProperty encryptedJsonSchemaProperty = new EncryptedJsonSchemaProperty(property);
|
||||
if (key != null) {
|
||||
targetPropertyList.add(encryptedJsonSchemaProperty.keyId(key));
|
||||
if (keyId != null) {
|
||||
targetPropertyList.add(encryptedJsonSchemaProperty.keyId(keyId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,47 +803,50 @@ public class CollectionOptions {
|
||||
|
||||
List<Document> converted = new ArrayList<>(properties.size());
|
||||
for (JsonSchemaProperty property : properties) {
|
||||
|
||||
Document field = new Document("path", property.getIdentifier());
|
||||
|
||||
if (!property.getTypes().isEmpty()) {
|
||||
field.append("bsonType", property.getTypes().iterator().next().toBsonType().value());
|
||||
}
|
||||
|
||||
if (property instanceof QueryableJsonSchemaProperty qproperty && qproperty
|
||||
.getTargetProperty() instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted) {
|
||||
if (encrypted.getKeyId() != null) {
|
||||
if (encrypted.getKeyId() instanceof String stringKey) {
|
||||
field.append("keyId",
|
||||
new BsonBinary(BsonBinarySubType.UUID_STANDARD, stringKey.getBytes(StandardCharsets.UTF_8)));
|
||||
} else {
|
||||
field.append("keyId", encrypted.getKeyId());
|
||||
}
|
||||
}
|
||||
} else if (property instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted) {
|
||||
if (encrypted.getKeyId() != null) {
|
||||
if (encrypted.getKeyId() instanceof String stringKey) {
|
||||
field.append("keyId",
|
||||
new BsonBinary(BsonBinarySubType.UUID_STANDARD, stringKey.getBytes(StandardCharsets.UTF_8)));
|
||||
} else {
|
||||
field.append("keyId", encrypted.getKeyId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (property instanceof QueryableJsonSchemaProperty qproperty) {
|
||||
field.append("queries", StreamSupport.stream(qproperty.getCharacteristics().spliterator(), false)
|
||||
.map(QueryCharacteristic::toDocument).toList());
|
||||
}
|
||||
if (!field.containsKey("keyId")) {
|
||||
field.append("keyId", BsonNull.VALUE);
|
||||
}
|
||||
|
||||
converted.add(field);
|
||||
converted.add(getEncryptedField(property));
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
private Document getEncryptedField(JsonSchemaProperty property) {
|
||||
|
||||
Document field = new Document("path", property.getIdentifier());
|
||||
|
||||
if (!property.getTypes().isEmpty()) {
|
||||
field.append("bsonType", property.getTypes().iterator().next().toBsonType().value());
|
||||
}
|
||||
|
||||
if (property instanceof QueryableJsonSchemaProperty qp
|
||||
&& qp.getTargetProperty() instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted
|
||||
&& encrypted.getKeyId() != null) {
|
||||
|
||||
if (encrypted.getKeyId() instanceof String stringKey) {
|
||||
field.append("keyId",
|
||||
new BsonBinary(BsonBinarySubType.UUID_STANDARD, stringKey.getBytes(StandardCharsets.UTF_8)));
|
||||
} else {
|
||||
field.append("keyId", encrypted.getKeyId());
|
||||
}
|
||||
} else if (property instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted
|
||||
&& encrypted.getKeyId() != null) {
|
||||
if (encrypted.getKeyId() instanceof String stringKey) {
|
||||
field.append("keyId",
|
||||
new BsonBinary(BsonBinarySubType.UUID_STANDARD, stringKey.getBytes(StandardCharsets.UTF_8)));
|
||||
} else {
|
||||
field.append("keyId", encrypted.getKeyId());
|
||||
}
|
||||
}
|
||||
|
||||
if (property instanceof QueryableJsonSchemaProperty qp) {
|
||||
field.append("queries", qp.getCharacteristics().map(QueryCharacteristic::toDocument).toList());
|
||||
}
|
||||
|
||||
if (!field.containsKey("keyId")) {
|
||||
field.append("keyId", BsonNull.VALUE);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
private List<Document> fromSchema() {
|
||||
|
||||
if (schema == null) {
|
||||
@@ -851,19 +858,25 @@ public class CollectionOptions {
|
||||
collectPaths(root, null, paths);
|
||||
|
||||
List<Document> fields = new ArrayList<>();
|
||||
if (!paths.isEmpty()) {
|
||||
if (paths.isEmpty()) {
|
||||
return fields;
|
||||
}
|
||||
|
||||
for (Entry<String, Document> entry : paths.entrySet()) {
|
||||
Document field = new Document("path", entry.getKey());
|
||||
field.append("keyId", entry.getValue().getOrDefault("keyId", BsonNull.VALUE));
|
||||
if (entry.getValue().containsKey("bsonType")) {
|
||||
field.append("bsonType", entry.getValue().get("bsonType"));
|
||||
}
|
||||
if (entry.getValue().containsKey("queries")) {
|
||||
field.put("queries", entry.getValue().get("queries"));
|
||||
}
|
||||
fields.add(field);
|
||||
for (Entry<String, Document> entry : paths.entrySet()) {
|
||||
|
||||
Document field = new Document("path", entry.getKey());
|
||||
|
||||
field.append("keyId", entry.getValue().getOrDefault("keyId", BsonNull.VALUE));
|
||||
|
||||
if (entry.getValue().containsKey("bsonType")) {
|
||||
field.append("bsonType", entry.getValue().get("bsonType"));
|
||||
}
|
||||
|
||||
if (entry.getValue().containsKey("queries")) {
|
||||
field.put("queries", entry.getValue().get("queries"));
|
||||
}
|
||||
|
||||
fields.add(field);
|
||||
}
|
||||
|
||||
return fields;
|
||||
@@ -873,28 +886,29 @@ public class CollectionOptions {
|
||||
private static void collectPaths(Document document, @Nullable String currentPath, Map<String, Document> paths) {
|
||||
|
||||
if (document.containsKey("type") && document.get("type").equals("object")) {
|
||||
|
||||
Object o = document.get("properties");
|
||||
if (o == null) {
|
||||
|
||||
if (!(o instanceof Document properties)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (o instanceof Document properties) {
|
||||
for (Entry<String, Object> entry : properties.entrySet()) {
|
||||
if (entry.getValue() instanceof Document nested) {
|
||||
for (Entry<String, Object> entry : properties.entrySet()) {
|
||||
|
||||
String path = currentPath == null ? entry.getKey() : (currentPath + "." + entry.getKey());
|
||||
if (nested.containsKey("encrypt")) {
|
||||
Document target = new Document(nested.get("encrypt", Document.class));
|
||||
if (nested.containsKey("queries")) {
|
||||
List<?> queries = nested.get("queries", List.class);
|
||||
if (!queries.isEmpty() && queries.iterator().next() instanceof Document qd) {
|
||||
target.putAll(qd);
|
||||
}
|
||||
if (entry.getValue() instanceof Document nested) {
|
||||
|
||||
String path = currentPath == null ? entry.getKey() : (currentPath + "." + entry.getKey());
|
||||
if (nested.containsKey("encrypt")) {
|
||||
Document target = new Document(nested.get("encrypt", Document.class));
|
||||
if (nested.containsKey("queries")) {
|
||||
List<?> queries = nested.get("queries", List.class);
|
||||
if (!queries.isEmpty() && queries.iterator().next() instanceof Document qd) {
|
||||
target.putAll(qd);
|
||||
}
|
||||
paths.put(path, target);
|
||||
} else {
|
||||
collectPaths(nested, path, paths);
|
||||
}
|
||||
paths.put(path, target);
|
||||
} else {
|
||||
collectPaths(nested, path, paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.bson.Document;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.domain.Range.Bound;
|
||||
import org.springframework.data.util.Streamable;
|
||||
|
||||
/**
|
||||
* Encapsulation of individual {@link QueryCharacteristic query characteristics} used to define queries that can be
|
||||
@@ -33,7 +34,7 @@ import org.springframework.data.domain.Range.Bound;
|
||||
* @author Christoph Strobl
|
||||
* @since 4.5
|
||||
*/
|
||||
public class QueryCharacteristics implements Iterable<QueryCharacteristic> {
|
||||
public class QueryCharacteristics implements Streamable<QueryCharacteristic> {
|
||||
|
||||
/**
|
||||
* instance indicating none
|
||||
|
||||
Reference in New Issue
Block a user