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 {

Reply via email to