Uplift of json schema validator library 16/138516/1
authoradheli.tavares <adheli.tavares@est.tech>
Fri, 19 Jul 2024 10:51:37 +0000 (11:51 +0100)
committeradheli.tavares <adheli.tavares@est.tech>
Fri, 19 Jul 2024 10:55:19 +0000 (11:55 +0100)
Issue-ID: POLICY-5084
Change-Id: Ie24719570c43a9f7b0fdac28973cc50d8eb7ed2c
Signed-off-by: adheli.tavares <adheli.tavares@est.tech>
context/context-management/src/main/java/org/onap/policy/apex/context/impl/schema/SchemaHelperFactory.java
examples/examples-grpc/src/test/java/org/onap/policy/apex/examples/grpc/GrpcTestRestSimEndpoint.java
examples/examples-grpc/src/test/java/org/onap/policy/apex/examples/grpc/GrpcTestServerSim.java
examples/examples-grpc/src/test/java/org/onap/policy/apex/examples/grpc/TestApexGrpcExample.java
model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyLogicReader.java
plugins/plugins-context/plugins-context-schema/plugins-context-schema-json/pom.xml
plugins/plugins-context/plugins-context-schema/plugins-context-schema-json/src/main/java/org/onap/policy/apex/plugins/context/schema/json/JsonSchemaHelper.java
plugins/plugins-context/plugins-context-schema/plugins-context-schema-json/src/test/java/org/onap/policy/apex/plugins/context/schema/json/JsonSchemaHelperMarshalTest.java
plugins/plugins-context/plugins-context-schema/plugins-context-schema-json/src/test/java/org/onap/policy/apex/plugins/context/schema/json/JsonSchemaHelperUnmarshalTest.java
services/services-engine/src/test/java/org/onap/policy/apex/service/engine/runtime/impl/EngineServiceImplTest.java
services/services-engine/src/test/java/org/onap/policy/apex/service/engine/runtime/impl/EngineWorkerTest.java

index 08289e6..3602ce1 100644 (file)
@@ -1,7 +1,7 @@
 /*-
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
- *  Modifications Copyright (C) 2019-2020 Nordix Foundation.
+ *  Modifications Copyright (C) 2019-2020, 2024 Nordix Foundation.
  *  Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -84,7 +84,7 @@ public class SchemaHelperFactory {
         }
 
         // Get the class for the schema helper using reflection
-        Object schemaHelperObject = null;
+        Object schemaHelperObject;
         final String pluginClass = schemaHelperParameters.getSchemaHelperPluginClass();
         try {
             schemaHelperObject = Class.forName(pluginClass).getDeclaredConstructor().newInstance();
@@ -96,7 +96,7 @@ public class SchemaHelperFactory {
         }
 
         // Check the class is a schema helper
-        if (!(schemaHelperObject instanceof SchemaHelper)) {
+        if (!(schemaHelperObject instanceof SchemaHelper schemaHelper)) {
             final var resultString = "Specified Apex context schema helper plugin class \"" + pluginClass
                     + "\" does not implement the SchemaHelper interface";
             LOGGER.warn(resultString);
@@ -104,7 +104,6 @@ public class SchemaHelperFactory {
         }
 
         // The context schema helper to return
-        final var schemaHelper = (SchemaHelper) schemaHelperObject;
 
         // Lock and load the schema helper
         schemaHelper.init(owningEntityKey.getKey(), schema);
index 3d46e6a..56539a1 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2020-2023 Nordix Foundation.
+ *  Copyright (C) 2020-2024 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@
 
 package org.onap.policy.apex.examples.grpc;
 
+import static org.awaitility.Awaitility.await;
+
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.POST;
 import jakarta.ws.rs.Path;
@@ -28,6 +30,7 @@ import jakarta.ws.rs.core.Response;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
 import org.slf4j.ext.XLogger;
 import org.slf4j.ext.XLoggerFactory;
 
@@ -52,8 +55,12 @@ public class GrpcTestRestSimEndpoint {
     public Response dcaeClOutput(@QueryParam("timeout") final int timeout) throws IOException {
         String createSubscriptionRequest =
             Files.readString(Paths.get("src/main/resources/examples/events/APEXgRPC/CreateSubscriptionEvent.json"));
-        LOGGER.info("Create subscription request received: \n {}", createSubscriptionRequest);
+        LOGGER.info("Create subscription request received (on a timeout of {}): \n {} ",
+            timeout, createSubscriptionRequest);
 
+        await().pollDelay(4, TimeUnit.SECONDS)
+            .atMost(5, TimeUnit.SECONDS)
+            .until(() -> true);
         return Response.status(200).entity(createSubscriptionRequest).build();
     }
 
index 46e0ea2..4dc6385 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2020,2023 Nordix Foundation.
+ *  Copyright (C) 2020, 2023-2024 Nordix Foundation.
  *  Modifications Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,11 +33,11 @@ import org.onap.policy.simulators.CdsSimulator;
  */
 public class GrpcTestServerSim {
     private static final String HOST = "localhost";
-    private HttpServletServer restServer;
-    private CdsSimulator grpcServer;
+    private final HttpServletServer restServer;
+    private final CdsSimulator grpcServer;
 
     /**
-     * Instantiates a new REST simulator for DMaaP requests.
+     * Instantiates a new REST simulator for requests.
      *
      * @throws InterruptedException interrupted exception
      * @throws IOException io exception
index ee1744f..e6d71e1 100644 (file)
@@ -41,6 +41,7 @@ import org.onap.policy.apex.service.engine.main.ApexMain;
  * CDS is used to send a final output Log event on POLICY_CL_MGT topic.
  */
 class TestApexGrpcExample {
+
     @Test
     void testGrpcExample() throws Exception {
         // @formatter:off
@@ -78,18 +79,24 @@ class TestApexGrpcExample {
 
         String getLoggedEventUrl = "http://localhost:54321/GrpcTestRestSim/sim/event/getLoggedEvent";
         // wait for success response code to be received, until a timeout
-        await().atMost(20000, TimeUnit.MILLISECONDS).until(() ->
-            200 == client.target(getLoggedEventUrl).request("application/json").get().getStatus());
+        await().atMost(50000, TimeUnit.MILLISECONDS)
+            .pollInterval(10000, TimeUnit.MILLISECONDS)
+            .until(() -> 200 == client.target(getLoggedEventUrl).request("application/json").get().getStatus());
+
         apexMain.shutdown();
+
         Response response = client.target(getLoggedEventUrl).request("application/json").get();
         sim.tearDown();
+
         String responseEntity = response.readEntity(String.class);
-        String expectedLoggedOutputEvent = Files
-            .readString(Paths.get("src/main/resources/examples/events/APEXgRPC/LogEvent.json")).replaceAll("\r", "");
-        String expectedStatusEvent =
-            Files.readString(Paths.get("src/main/resources/examples/events/APEXgRPC/CDSResponseStatusEvent.json"))
-                .replaceAll("\r", "");
+        var logFileJson = "src/main/resources/examples/events/APEXgRPC/LogEvent.json";
+        String expectedLoggedOutputEvent = Files.readString(Paths.get(logFileJson)).replaceAll("\r", "");
+
+        var cdsResponseJson = "src/main/resources/examples/events/APEXgRPC/CDSResponseStatusEvent.json";
+        String expectedStatusEvent = Files.readString(Paths.get(cdsResponseJson)).replaceAll("\r", "");
+
         // Both LogEvent and CDSResponseStatusEvent are generated from the final state in the policy
         assertThat(responseEntity).contains(expectedStatusEvent).contains(expectedLoggedOutputEvent);
+        client.close();
     }
 }
\ No newline at end of file
index 8b0dec7..4a8780a 100644 (file)
@@ -23,7 +23,6 @@ package org.onap.policy.apex.model.policymodel.handling;
 
 import static org.onap.policy.apex.model.basicmodel.concepts.AxConcept.WHITESPACE_REGEX;
 
-import org.jetbrains.annotations.NotNull;
 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
 import org.onap.policy.apex.model.policymodel.concepts.AxLogic;
 import org.onap.policy.apex.model.policymodel.concepts.AxLogicReader;
@@ -124,7 +123,7 @@ public class PolicyLogicReader implements AxLogicReader {
         return logicString.replaceAll(WHITESPACE_REGEX, "");
     }
 
-    private @NotNull String getFullLogicFilePath(AxLogic axLogic) {
+    private String getFullLogicFilePath(AxLogic axLogic) {
         String fullLogicFilePath = logicPackage.replace(".", "/");
 
         // Now, the logic should be in a subdirectory for the logic executor type
index c46b262..99864b3 100644 (file)
@@ -1,7 +1,7 @@
 <!--
   ============LICENSE_START=======================================================
    Copyright (C) 2022 Bell Canada. All rights reserved.
-   Modifications Copyright (C) 2023 Nordix Foundation.
+   Modifications Copyright (C) 2023-2024 Nordix Foundation.
   ================================================================================
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -33,8 +33,8 @@
 
     <dependencies>
         <dependency>
-            <groupId>com.worldturner.medeia</groupId>
-            <artifactId>medeia-validator-gson</artifactId>
+            <groupId>com.networknt</groupId>
+            <artifactId>json-schema-validator</artifactId>
         </dependency>
     </dependencies>
 </project>
index 4896e5d..0e8d5df 100644 (file)
@@ -1,6 +1,7 @@
 /*-
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada. All rights reserved.
+ * Modifications Copyright (C) 2024 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,18 +23,18 @@ package org.onap.policy.apex.plugins.context.schema.json;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonElement;
-import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonWriter;
-import com.worldturner.medeia.api.SchemaSource;
-import com.worldturner.medeia.api.StringInputSource;
-import com.worldturner.medeia.api.StringSchemaSource;
-import com.worldturner.medeia.api.gson.MedeiaGsonApi;
-import com.worldturner.medeia.schema.validation.SchemaValidator;
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SpecVersionDetector;
+import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.List;
 import java.util.Map;
 import org.onap.policy.apex.context.ContextRuntimeException;
 import org.onap.policy.apex.context.impl.schema.AbstractSchemaHelper;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException;
 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
 import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
 
@@ -44,16 +45,14 @@ public class JsonSchemaHelper extends AbstractSchemaHelper {
 
     private static final Gson gson = new Gson();
 
-    private MedeiaGsonApi api = new MedeiaGsonApi();
-    private SchemaValidator validator;
+    private JsonSchema jsonSchema;
 
     @Override
     public void init(final AxKey userKey, final AxContextSchema schema) {
         super.init(userKey, schema);
 
         try {
-            SchemaSource source = new StringSchemaSource(schema.getSchema());
-            validator = api.loadSchema(source);
+            this.jsonSchema = getJsonSchema(schema.getSchema());
         } catch (final Exception e) {
             final String resultSting = userKey.getId() + ": json context schema \"" + schema.getId()
                 + "\" schema is invalid, schema: " + schema.getSchema();
@@ -86,8 +85,8 @@ public class JsonSchemaHelper extends AbstractSchemaHelper {
             return object;
         }
         var objectString = (String) object;
-        JsonReader reader = api.createJsonReader(validator, new StringInputSource(objectString));
-        return gson.fromJson(reader, Object.class);
+        validate(objectString);
+        return gson.fromJson(new StringReader(objectString), Object.class);
     }
 
     @Override
@@ -103,7 +102,8 @@ public class JsonSchemaHelper extends AbstractSchemaHelper {
     }
 
     private JsonElement validateAndDecode(Object schemaObject, StringWriter stringWriter) {
-        JsonWriter jsonWriter = api.createJsonWriter(validator, stringWriter);
+        validate(schemaObject);
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
         jsonWriter.setIndent("  "); // to enable pretty print
         JsonElement jsonObj = gson.toJsonTree(schemaObject);
         gson.toJson(jsonObj, jsonWriter);
@@ -119,4 +119,27 @@ public class JsonSchemaHelper extends AbstractSchemaHelper {
     private boolean passThroughObject(final Object object) {
         return (object instanceof JsonElement || object instanceof Map || object instanceof List);
     }
+
+    private JsonSchema getJsonSchema(String jsonSchema) {
+        var schemaMap = new Gson().fromJson(jsonSchema, Map.class);
+        if (schemaMap != null && schemaMap.containsKey("$schema")) {
+            var flag = SpecVersionDetector.detectOptionalVersion(schemaMap.get("$schema").toString()).orElse(null);
+            return JsonSchemaFactory.getInstance(flag).getSchema(jsonSchema);
+        } else {
+            throw new ApexRuntimeException("Schema is invalid");
+        }
+    }
+
+    private void validate(String json) {
+        var validations = this.jsonSchema.validate(json, InputFormat.JSON);
+        if (!validations.isEmpty()) {
+            StringBuilder errors = new StringBuilder();
+            validations.forEach(m -> errors.append(m.toString()).append("\n"));
+            throw new ApexRuntimeException(errors.toString());
+        }
+    }
+
+    private void validate(Object object) {
+        validate(gson.toJson(object));
+    }
 }
index b129330..413037b 100644 (file)
@@ -26,7 +26,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
-import com.worldturner.medeia.api.ValidationFailedException;
 import java.util.ArrayList;
 import java.util.Map;
 import org.junit.jupiter.api.Test;
@@ -103,8 +102,7 @@ class JsonSchemaHelperMarshalTest extends CommonTestData {
         var dataAsObject = coder.decode(COMMONHEADER, Map.class);
         dataAsObject.remove(TEST_ID);
         assertThatThrownBy(() -> validateAndMarshal(COMMONHEADERTYPE_DRAFT07, dataAsObject, true))
-            .isInstanceOf(ValidationFailedException.class)
-            .hasMessageContaining("Required property testId is missing from object");
+            .hasMessageContaining("required property 'testId' not found");
     }
 
     /**
index d0059f2..f633b2d 100644 (file)
@@ -25,7 +25,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import com.google.gson.JsonObject;
-import com.worldturner.medeia.api.ValidationFailedException;
 import java.util.ArrayList;
 import java.util.Map;
 import org.junit.jupiter.api.Test;
@@ -74,7 +73,8 @@ class JsonSchemaHelperUnmarshalTest extends CommonTestData {
             new AxContextSchema(new AxArtifactKey("JsonObject", VERSION), JSON, schemaDef);
         var jsonSchemaHelper = new JsonSchemaHelper();
         assertThatThrownBy(() -> jsonSchemaHelper.init(testKey, jsonSchema))
-            .isInstanceOf(ContextRuntimeException.class).hasMessageContaining("schema is invalid");
+            .isInstanceOf(ContextRuntimeException.class)
+            .hasMessageContaining("schema is invalid");
     }
 
     /**
@@ -111,8 +111,7 @@ class JsonSchemaHelperUnmarshalTest extends CommonTestData {
         var dataAsObject = coder.decode(COMMONHEADER, JsonObject.class);
         dataAsObject.addProperty("requestId", "abcd");
         assertThatThrownBy(() -> validateAndUnmarshal(COMMONHEADERTYPE_DRAFT07, dataAsObject))
-            .isInstanceOf(ValidationFailedException.class)
-            .hasMessageContaining("Pattern ^[0-9]*-[0-9]*$ is not contained in text");
+            .hasMessageContaining("does not match the regex pattern ^[0-9]*-[0-9]*$");
     }
 
     /**
@@ -137,8 +136,7 @@ class JsonSchemaHelperUnmarshalTest extends CommonTestData {
         var dataAsObject = coder.decode(COMMONHEADER, JsonObject.class);
         dataAsObject.remove(TEST_ID);
         assertThatThrownBy(() -> validateAndUnmarshal(COMMONHEADERTYPE_DRAFT07, dataAsObject))
-            .isInstanceOf(ValidationFailedException.class)
-            .hasMessageContaining("Required property testId is missing from object");
+            .hasMessageContaining("required property 'testId' not found");
     }
 
     /**
index caba093..34db034 100644 (file)
@@ -32,7 +32,6 @@ import static org.junit.jupiter.api.Assertions.fail;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -118,7 +117,7 @@ class EngineServiceImplTest {
         ParameterService.register(engineParameters);
     }
 
-    private static @NotNull ExecutorParameters getExecutorParameters(String lang) {
+    private static ExecutorParameters getExecutorParameters(String lang) {
         ExecutorParameters jsExecutorParameters = new ExecutorParameters();
         jsExecutorParameters.setName(lang);
         jsExecutorParameters
index 0716e0a..5082100 100644 (file)
@@ -34,7 +34,6 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
-import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
@@ -125,7 +124,7 @@ class EngineWorkerTest {
 
     }
 
-    private static @NotNull ExecutorParameters getExecutorParameters(String lang) {
+    private static ExecutorParameters getExecutorParameters(String lang) {
         ExecutorParameters jsExecutorParameters = new ExecutorParameters();
         jsExecutorParameters.setName(lang);
         jsExecutorParameters