This is an automated email from the ASF dual-hosted git repository. shuber pushed a commit to branch poc-extendable in repository https://gitbox.apache.org/repos/asf/unomi.git
commit 17e1576a1d425858249bb23832327f078bb76475 Author: Serge Huber <[email protected]> AuthorDate: Tue May 3 19:41:24 2022 +0200 POC Extendable keyword --- .../apache/unomi/api/schema/json/JSONSchema.java | 24 +++++++ .../apache/unomi/api/services/SchemaService.java | 7 ++ .../services/impl/schemas/ExtendableKeyword.java | 74 ++++++++++++++++++++++ .../services/impl/schemas/SchemaServiceImpl.java | 4 ++ 4 files changed, 109 insertions(+) diff --git a/api/src/main/java/org/apache/unomi/api/schema/json/JSONSchema.java b/api/src/main/java/org/apache/unomi/api/schema/json/JSONSchema.java index 1e65f8e6e..34edd9600 100644 --- a/api/src/main/java/org/apache/unomi/api/schema/json/JSONSchema.java +++ b/api/src/main/java/org/apache/unomi/api/schema/json/JSONSchema.java @@ -33,6 +33,9 @@ public class JSONSchema extends JSONType implements PluginType { private String name; private String version; + private String extensionSchemaId; + private String extensionOperator; + public JSONSchema(Map<String, Object> schemaTree, JSONTypeFactory jsonTypeFactory) { super(schemaTree, jsonTypeFactory); schemaId = (String) schemaTree.get("$id"); @@ -42,6 +45,11 @@ public class JSONSchema extends JSONType implements PluginType { vendor = (String) self.get("vendor"); version = (String) self.get("version"); target = (String) self.get("target"); + if (self.containsKey("unomiExtends")) { + Map<String,Object> unomiExtends = (Map<String,Object>) self.get("unomiExtends"); + extensionSchemaId = (String) unomiExtends.get("schemaId"); + extensionOperator = (String) unomiExtends.get("operator"); + } } } @@ -93,6 +101,22 @@ public class JSONSchema extends JSONType implements PluginType { this.target = target; } + public String getExtensionSchemaId() { + return extensionSchemaId; + } + + public void setExtensionSchemaId(String extensionSchemaId) { + this.extensionSchemaId = extensionSchemaId; + } + + public String getExtensionOperator() { + return extensionOperator; + } + + public void setExtensionOperator(String extensionOperator) { + this.extensionOperator = extensionOperator; + } + public List<JSONType> getRootTypes() { if (rootTypes == null) { buildRootTypes(); diff --git a/api/src/main/java/org/apache/unomi/api/services/SchemaService.java b/api/src/main/java/org/apache/unomi/api/services/SchemaService.java index 7d6e65480..578e9f6ee 100644 --- a/api/src/main/java/org/apache/unomi/api/services/SchemaService.java +++ b/api/src/main/java/org/apache/unomi/api/services/SchemaService.java @@ -70,6 +70,13 @@ public interface SchemaService { */ List<JSONSchema> getSchemasByTarget(String target); + /** + * Return a list of schemas that are extensions of the given schema + * @param schemaId the ID of the schema to retrieve extensions for + * @return a list of schemas that extend the specified schemaId, or an empty list of no schemas extend it + */ + List<JSONSchema> getSchemaExtensions(String schemaId); + /** * Save a new schema or update a schema * diff --git a/services/src/main/java/org/apache/unomi/services/impl/schemas/ExtendableKeyword.java b/services/src/main/java/org/apache/unomi/services/impl/schemas/ExtendableKeyword.java new file mode 100644 index 000000000..6c0ada095 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/impl/schemas/ExtendableKeyword.java @@ -0,0 +1,74 @@ +package org.apache.unomi.services.impl.schemas; + +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.schema.*; +import org.apache.unomi.api.schema.json.JSONSchema; +import java.text.MessageFormat; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class ExtendableKeyword extends AbstractKeyword { + + SchemaServiceImpl schemaService; + + public class ExtendableJsonValidator extends AbstractJsonValidator { + + String schemaPath; + JsonNode schemaNode; + JsonSchema parentSchema; + ValidationContext validationContext; + SchemaServiceImpl schemaService; + + protected ExtendableJsonValidator(String keyword, String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, + ValidationContext validationContext, SchemaServiceImpl schemaService) { + super(keyword); + this.schemaPath = schemaPath; + this.schemaNode = schemaNode; + this.parentSchema = parentSchema; + this.validationContext = validationContext; + this.schemaService = schemaService; + } + + @Override + public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) { + String schemaId = rootNode.get("$id").asText(); + List<JSONSchema> schemaExtensions = schemaService.getSchemaExtensions(schemaId); + List<JSONSchema> andExtensions = schemaExtensions.stream().filter(schema -> schema.getExtensionOperator().equals("and")).collect(Collectors.toList()); + List<JSONSchema> orExtensions = schemaExtensions.stream().filter(schema -> schema.getExtensionOperator().equals("or")).collect(Collectors.toList()); + + Set<ValidationMessage> validationMessages = new LinkedHashSet<>(); + if (!andExtensions.stream().allMatch(extensionSchema -> { + Set<ValidationMessage> andValidationMessages = schemaService.getJsonSchema(extensionSchema.getSchemaId()).validate(node); + validationMessages.addAll(andValidationMessages); + return andValidationMessages.size() == 0; + })) { + validationMessages.add(buildValidationMessage(CustomErrorMessageType + .of("and-schema-not-valid", new MessageFormat("{0} : invalid values for extension of id={1}")), at, + schemaId)); + } + if (!orExtensions.stream().anyMatch(extensionSchema -> { + Set<ValidationMessage> anyValidationMessages = schemaService.getJsonSchema(extensionSchema.getSchemaId()).validate(node); + validationMessages.addAll(anyValidationMessages); + return anyValidationMessages.size() == 0; + })) { + validationMessages.add(buildValidationMessage(CustomErrorMessageType + .of("or-schema-not-valid", new MessageFormat("{0} : invalid values for extension of id={1}")), at, + schemaId)); + } + return validationMessages; + } + } + + public ExtendableKeyword(SchemaServiceImpl schemaService) { + super("extendable"); + this.schemaService = schemaService; + } + + @Override + public JsonValidator newValidator(String schemaPath, JsonNode jsonNode, JsonSchema jsonSchema, ValidationContext validationContext) throws JsonSchemaException, Exception { + return new ExtendableJsonValidator(this.getValue(), schemaPath, jsonNode, jsonSchema, validationContext, + schemaService); + } +} diff --git a/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaServiceImpl.java b/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaServiceImpl.java index dc6a339b9..70141daf6 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaServiceImpl.java +++ b/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaServiceImpl.java @@ -236,6 +236,10 @@ public class SchemaServiceImpl implements SchemaService { return schemasById.get(schemaId); } + public List<JSONSchema> getSchemaExtensions(String schemaId) { + return schemasById.values().stream().filter(schema -> schema.getExtensionSchemaId().equals(schemaId)).collect(Collectors.toList()); + } + private JSONSchema buildJSONSchema(JsonSchema jsonSchema) { return Optional.of(jsonSchema).map(jsonSchemaToProcess -> { try {
