diff options
Diffstat (limited to 'utils/src')
10 files changed, 215 insertions, 146 deletions
diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/CoderException.java b/utils/src/main/java/org/onap/policy/common/utils/coder/CoderException.java index 60e85733..8390d175 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/CoderException.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/CoderException.java @@ -1,8 +1,7 @@ /* * ============LICENSE_START======================================================= - * ONAP PAP - * ================================================================================ * Copyright (C) 2019 AT&T Intellectual Property. 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. @@ -20,10 +19,14 @@ package org.onap.policy.common.utils.coder; +import java.io.Serial; + /** * Exceptions generated by coders. */ public class CoderException extends Exception { + + @Serial private static final long serialVersionUID = 1L; public CoderException() { diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/CoderRuntimeException.java b/utils/src/main/java/org/onap/policy/common/utils/coder/CoderRuntimeException.java new file mode 100644 index 00000000..0ffd6077 --- /dev/null +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/CoderRuntimeException.java @@ -0,0 +1,35 @@ +/* + * ============LICENSE_START======================================================= + * 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. + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.coder; + +import java.io.Serial; + +/** + * Exceptions generated by coders. + */ +public class CoderRuntimeException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + public CoderRuntimeException(Throwable cause) { + super(cause); + } + +} diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java index d6135afd..834a8504 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java @@ -3,6 +3,7 @@ * ONAP * ================================================================================ * Copyright (C) 2019-2021 AT&T Intellectual Property. 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. @@ -179,7 +180,6 @@ public class StandardCoder implements Coder { public <T> T decode(String json, Class<T> clazz) throws CoderException { try { return fromJson(json, clazz); - } catch (RuntimeException e) { throw new CoderException(e); } @@ -378,7 +378,7 @@ public class StandardCoder implements Coder { /** * Used to read/write a JsonElement. */ - private static TypeAdapter<JsonElement> elementAdapter = new Gson().getAdapter(JsonElement.class); + private static final TypeAdapter<JsonElement> elementAdapter = new Gson().getAdapter(JsonElement.class); @Override public void write(JsonWriter out, StandardCoderObject value) throws IOException { diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoderObject.java b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoderObject.java index 55f7f9d7..5402f37b 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoderObject.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoderObject.java @@ -3,6 +3,7 @@ * ONAP * ================================================================================ * Copyright (C) 2019-2021 AT&T Intellectual Property. 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. @@ -21,6 +22,7 @@ package org.onap.policy.common.utils.coder; import com.google.gson.JsonElement; +import java.io.Serial; import java.io.Serializable; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -33,6 +35,8 @@ import lombok.Getter; */ @AllArgsConstructor(access = AccessLevel.PROTECTED) public class StandardCoderObject implements Serializable { + + @Serial private static final long serialVersionUID = 1L; /** @@ -56,9 +60,8 @@ public class StandardCoderObject implements Serializable { * Gets a field's value from this object, traversing the object hierarchy. * * @param fields field hierarchy. These may be strings, identifying fields within the - * object, or Integers, identifying an index within an array - * @return the field value or {@code null} if the field does not exist or is not a - * primitive + * object, or Integers, identifying an index within an array + * @return the field value or {@code null} if the field does not exist or is not a primitive */ public String getString(Object... fields) { @@ -87,9 +90,8 @@ public class StandardCoderObject implements Serializable { * Gets an item from an object. * * @param element object from which to extract the item - * @param field name of the field from which to extract the item - * @return the item, or {@code null} if the element is not an object or if the field - * does not exist + * @param field name of the field from which to extract the item + * @return the item, or {@code null} if the element is not an object or if the field does not exist */ protected JsonElement getFieldFromObject(JsonElement element, String field) { if (!element.isJsonObject()) { @@ -103,9 +105,8 @@ public class StandardCoderObject implements Serializable { * Gets an item from an array. * * @param element array from which to extract the item - * @param index index of the item to extract - * @return the item, or {@code null} if the element is not an array or if the index is - * out of bounds + * @param index index of the item to extract + * @return the item, or {@code null} if the element is not an array or if the index is out of bounds */ protected JsonElement getItemFromArray(JsonElement element, int index) { if (index < 0) { diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardValCoder.java b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardValCoder.java index 4deeba14..408ae81e 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardValCoder.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardValCoder.java @@ -1,6 +1,7 @@ /*-- * ============LICENSE_START======================================================= * Copyright (C) 2020-2021 AT&T Intellectual Property. 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. @@ -20,18 +21,17 @@ package org.onap.policy.common.utils.coder; -import com.worldturner.medeia.api.JsonSchemaVersion; -import com.worldturner.medeia.api.SchemaSource; -import com.worldturner.medeia.api.StringSchemaSource; -import com.worldturner.medeia.api.ValidationFailedException; -import com.worldturner.medeia.api.gson.MedeiaGsonApi; -import com.worldturner.medeia.schema.validation.SchemaValidator; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import lombok.NonNull; import lombok.ToString; +import net.jimblackler.jsonschemafriend.GenerationException; +import net.jimblackler.jsonschemafriend.Schema; +import net.jimblackler.jsonschemafriend.SchemaStore; +import net.jimblackler.jsonschemafriend.ValidationException; +import net.jimblackler.jsonschemafriend.Validator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,31 +42,29 @@ import org.slf4j.LoggerFactory; @ToString public class StandardValCoder extends StandardCoder { - // The medeia-validator library integrates better than - // other libraries considered with GSON, and therefore - // the StandardCoder. - private static final Logger logger = LoggerFactory.getLogger(StandardValCoder.class); - private final MedeiaGsonApi validatorApi = new MedeiaGsonApi(); - private final SchemaValidator validator; + private final Schema schema; /** * StandardCoder with validation. */ - public StandardValCoder(@NonNull String jsonSchema, @NonNull String name) { - SchemaSource schemaSource = new StringSchemaSource(jsonSchema, JsonSchemaVersion.DRAFT07, null, name); - this.validator = validatorApi.loadSchema(schemaSource); + public StandardValCoder(@NonNull String jsonSchema) { + try { + SchemaStore store = new SchemaStore(); + this.schema = store.loadSchemaJson(jsonSchema); + } catch (GenerationException e) { + throw new CoderRuntimeException(e); + } } @Override protected String toPrettyJson(Object object) { - /* - * The validator strips off the "pretty" stuff (i.e., spaces), thus we have to validate and generate the pretty - * JSON in separate steps. - */ - gson.toJson(object, object.getClass(), validatorApi.createJsonWriter(validator, new StringWriter())); - + try { + validate(gsonPretty.toJson(object)); + } catch (CoderException e) { + throw new CoderRuntimeException(e); + } return super.toPrettyJson(object); } @@ -79,18 +77,28 @@ public class StandardValCoder extends StandardCoder { @Override protected void toJson(@NonNull Writer target, @NonNull Object object) { - gson.toJson(object, object.getClass(), validatorApi.createJsonWriter(validator, target)); + try { + validate(gson.toJson(object)); + } catch (CoderException e) { + throw new CoderRuntimeException(e); + } + gson.toJson(object, object.getClass(), target); } @Override protected <T> T fromJson(@NonNull Reader source, @NonNull Class<T> clazz) { - return convertFromDouble(clazz, gson.fromJson(validatorApi.createJsonReader(validator, source), clazz)); + return convertFromDouble(clazz, gson.fromJson(source, clazz)); } @Override protected <T> T fromJson(String json, Class<T> clazz) { + try { + validate(json); + } catch (CoderException e) { + throw new CoderRuntimeException(e); + } var reader = new StringReader(json); - return convertFromDouble(clazz, gson.fromJson(validatorApi.createJsonReader(validator, reader), clazz)); + return convertFromDouble(clazz, gson.fromJson(reader, clazz)); } /** @@ -99,21 +107,32 @@ public class StandardValCoder extends StandardCoder { public boolean isConformant(@NonNull String json) { try { conformance(json); - } catch (CoderException e) { - logger.info("JSON is not conformant to schema", e); + return true; + } catch (Exception e) { + logger.error("JSON is not conformant to schema", e); return false; } - return true; } /** * Check a json string for conformance against its schema definition. */ public void conformance(@NonNull String json) throws CoderException { + validate(json); + } + + private void validate(Object object) throws CoderException { try { - validatorApi.parseAll(validatorApi.createJsonReader(validator, new StringReader(json))); - } catch (ValidationFailedException e) { - throw new CoderException(e); + final var validator = new Validator(); + validator.validate(schema, object); + } catch (ValidationException exception) { + var error = String.format("JSON validation failed: %s", exception.getMessage()); + logger.error(error); + throw new CoderException(error); } } + + private void validate(String json) throws CoderException { + validate(gson.fromJson(json, Object.class)); + } } diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardYamlCoder.java b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardYamlCoder.java index d94ddca4..8ee2e81d 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardYamlCoder.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardYamlCoder.java @@ -3,6 +3,7 @@ * ONAP * ================================================================================ * Copyright (C) 2019-2020 AT&T Intellectual Property. 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. @@ -24,8 +25,8 @@ import java.io.Reader; import java.io.Writer; /** - * YAML encoder and decoder using the "standard" mechanism, which is currently gson. All - * of the methods perform conversion to/from YAML (instead of JSON). + * YAML encoder and decoder using the "standard" mechanism, which is currently gson. + * All the methods perform conversion to/from YAML (instead of JSON). */ public class StandardYamlCoder extends StandardCoder { private final YamlJsonTranslator translator; diff --git a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderInstantAsMillisTest.java b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderInstantAsMillisTest.java index f13ddd9a..6db9e66e 100644 --- a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderInstantAsMillisTest.java +++ b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderInstantAsMillisTest.java @@ -87,7 +87,7 @@ class StandardCoderInstantAsMillisTest { } @Test - void testToJsonTree_testFromJsonJsonElementClassT() throws Exception { + void testToJsonTree_testFromJsonJsonElementClassT() { MyMap map = new MyMap(); map.props = new LinkedHashMap<>(); map.props.put("jel keyA", "jel valueA"); diff --git a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardValCoderTest.java b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardValCoderTest.java index 45d814a4..8b5f406b 100644 --- a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardValCoderTest.java +++ b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardValCoderTest.java @@ -21,24 +21,23 @@ package org.onap.policy.common.utils.coder; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.worldturner.medeia.api.ValidationFailedException; -import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; import lombok.Data; import lombok.NoArgsConstructor; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.onap.policy.common.utils.resources.ResourceUtils; class StandardValCoderTest { private String jsonSchema; @@ -63,7 +62,7 @@ class StandardValCoderTest { } @BeforeEach - public void testSetUp() throws Exception { + public void testSetUp() { jsonSchema = getJson("src/test/resources/org/onap/policy/common/utils/coder/test.schema.json"); validJson = getJson("src/test/resources/org/onap/policy/common/utils/coder/valid.json"); missingReqJson = getJson("src/test/resources/org/onap/policy/common/utils/coder/missing-required.json"); @@ -72,7 +71,7 @@ class StandardValCoderTest { @Test void testDecode() throws CoderException { - StandardValCoder valCoder = new StandardValCoder(jsonSchema, "test-schema"); + StandardValCoder valCoder = new StandardValCoder(jsonSchema); ValOuter valOuter = valCoder.decode(validJson, ValOuter.class); assertValidJson(valOuter); @@ -85,34 +84,22 @@ class StandardValCoderTest { valCoder.decode(missingReqJson, ValOuter.class); fail("missing required field should have been flagged by the schema validation"); } catch (CoderException e) { - assertEquals("required", ((ValidationFailedException) e.getCause()).getFailures().get(0).getRule()); - assertEquals("aaCollection", - ((ValidationFailedException) e.getCause()).getFailures().get(0).getProperty()); - assertEquals("Required property aaCollection is missing from object", - ((ValidationFailedException) e.getCause()).getFailures().get(0).getMessage()); + assertThat(e.getMessage()).contains("Missing property aaCollection"); } try { valCoder.decode(badRegexJson, ValOuter.class); fail("bad regex should have been flagged by the schema validation"); } catch (CoderException e) { - assertEquals("properties", ((ValidationFailedException) e.getCause()).getFailures().get(0).getRule()); - assertEquals("aaString", - ((ValidationFailedException) e.getCause()).getFailures().get(0).getProperty()); - assertEquals("Property validation failed", - ((ValidationFailedException) e.getCause()).getFailures().get(0).getMessage()); - assertEquals("pattern", - ((ValidationFailedException) e.getCause()).getFailures() - .get(0).getDetails().iterator().next().getRule()); - assertEquals("Pattern ^([a-z]*)$ is not contained in text", - ((ValidationFailedException) e.getCause()).getFailures() - .get(0).getDetails().iterator().next().getMessage()); + assertThat(e.getMessage()) + .contains("Validation errors: \"abc123\" at #/aaString failed") + .contains("Did not match pattern: ^([a-z]*)$"); } } @Test void testEncode() throws CoderException { - StandardValCoder valCoder = new StandardValCoder(jsonSchema, "test-schema"); + StandardValCoder valCoder = new StandardValCoder(jsonSchema); ValOuter valOuter = valCoder.decode(validJson, ValOuter.class); String valOuterJson = valCoder.encode(valOuter); @@ -129,7 +116,7 @@ class StandardValCoderTest { @Test void testPretty() throws CoderException { - StandardValCoder valCoder = new StandardValCoder(jsonSchema, "test-schema"); + StandardValCoder valCoder = new StandardValCoder(jsonSchema); ValOuter valOuter = valCoder.decode(validJson, ValOuter.class); String valOuterJson = valCoder.encode(valOuter); @@ -146,12 +133,33 @@ class StandardValCoderTest { @Test void testConformance() { - StandardValCoder valCoder = new StandardValCoder(jsonSchema, "test-schema"); + StandardValCoder valCoder = new StandardValCoder(jsonSchema); assertTrue(valCoder.isConformant(validJson)); assertFalse(valCoder.isConformant(missingReqJson)); assertFalse(valCoder.isConformant(badRegexJson)); } + @Test + void testNullValues() throws CoderException { + StandardValCoder valCoder = new StandardValCoder(jsonSchema); + assertThrows(NullPointerException.class, () -> valCoder.toJson(null)); + assertThrows(NullPointerException.class, () -> valCoder.isConformant(null)); + assertThrows(NullPointerException.class, () -> valCoder.conformance(null)); + + assertThrows(NullPointerException.class, () -> valCoder.toJson(null, null)); + var writer = new StringWriter(); + assertThrows(NullPointerException.class, () -> valCoder.toJson(writer, null)); + ValOuter valOuter = valCoder.decode(validJson, ValOuter.class); + assertThrows(NullPointerException.class, () -> valCoder.toJson(null, valOuter)); + } + + @Test + void testConstructor() { + assertThrows(NullPointerException.class, () -> new StandardValCoder(null)); + + assertThrows(CoderRuntimeException.class, () -> new StandardValCoder("$schema")); + } + private void assertValidJson(ValOuter valOuter) { assertEquals("abcd", valOuter.getAaString()); assertEquals(90, valOuter.getAnInteger()); @@ -160,7 +168,7 @@ class StandardValCoderTest { assertEquals(Integer.valueOf(1200), valOuter.getAaCollection().get(0).getSubItemInteger()); } - private String getJson(String filePath) throws IOException { - return new String(Files.readAllBytes(Paths.get(filePath))); + private String getJson(String filePath) { + return ResourceUtils.getResourceAsString(filePath); } }
\ No newline at end of file diff --git a/utils/src/test/resources/org/onap/policy/common/utils/coder/test.schema.json b/utils/src/test/resources/org/onap/policy/common/utils/coder/test.schema.json index e79475eb..60e70fb4 100644 --- a/utils/src/test/resources/org/onap/policy/common/utils/coder/test.schema.json +++ b/utils/src/test/resources/org/onap/policy/common/utils/coder/test.schema.json @@ -1,71 +1,71 @@ { - "definitions": {}, - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "http://onap.org/policy/common/coders/root.json", - "type": "object", - "title": "Test Schema", - "required": [ - "aaString", - "anInteger", - "aaBoolean", - "aaCollection" - ], - "properties": { - "aaString": { - "$id": "#/properties/aaString", + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://onap.org/policy/common/coders/root.json", + "type": "object", + "title": "Test Schema", + "required": [ + "aaString", + "anInteger", + "aaBoolean", + "aaCollection" + ], + "properties": { + "aaString": { + "$id": "#/properties/aaString", + "type": "string", + "title": "an alphabetical string", + "default": "", + "examples": [ + "abcdef" + ], + "pattern": "^([a-z]*)$" + }, + "anInteger": { + "$id": "#/properties/anInteger", + "type": "integer", + "title": "a bounded integer", + "default": 5, + "examples": [ + 98 + ], + "minimum": 10, + "maximum": 100 + }, + "aaBoolean": { + "$id": "#/properties/aaBoolean", + "type": "boolean", + "title": "a boolean", + "default": false, + "examples": [ + true + ] + }, + "aaCollection": { + "$id": "#/properties/aaCollection", + "type": "array", + "title": "a collection", + "items": { + "$id": "#/properties/aaCollection/items", + "type": "object", + "title": "the collection items", + "required": [ + "subItemString" + ], + "properties": { + "subItemString": { + "$id": "#/properties/aaCollection/items/properties/subItemString", "type": "string", - "title": "an alphabetical string", - "default": "", - "examples": [ - "abcdef" - ], - "pattern": "^([a-z]*)$" - }, - "anInteger": { - "$id": "#/properties/anInteger", - "type": "integer", - "title": "a bounded integer", - "default": 5, - "examples": [ - 98 - ], - "minimum": 10, - "maximum": 100 - }, - "aaBoolean": { - "$id": "#/properties/aaBoolean", - "type": "boolean", - "title": "a boolean", - "default": false, - "examples": [ - true - ] - }, - "aaCollection": { - "$id": "#/properties/aaCollection", - "type": "array", - "title": "a collection", - "items": { - "$id": "#/properties/aaCollection/items", - "type": "object", - "title": "the collection items", - "required": [ - "subItemString" - ], - "properties": { - "subItemString": { - "$id": "#/properties/aaCollection/items/properties/subItemString", - "type": "string", - "title": "the subitem string", - "default": "blah", - "pattern": "^(.*)$" - }, - "subItemInteger": { - "$id": "#/properties/aaCollection/items/properties/subItemInteger", - "type": "integer" - } - } - } + "title": "the subitem string", + "default": "blah", + "pattern": "^(.*)$" + }, + "subItemInteger": { + "$id": "#/properties/aaCollection/items/properties/subItemInteger", + "type": "integer" + } } + } } + } }
\ No newline at end of file diff --git a/utils/src/test/resources/org/onap/policy/common/utils/coder/valid.json b/utils/src/test/resources/org/onap/policy/common/utils/coder/valid.json index c1738176..a2cdb932 100644 --- a/utils/src/test/resources/org/onap/policy/common/utils/coder/valid.json +++ b/utils/src/test/resources/org/onap/policy/common/utils/coder/valid.json @@ -1,9 +1,11 @@ { - "aaString": "abcd", - "anInteger": 90, - "aaBoolean": true, - "aaCollection": [ { - "subItemString": "defg", - "subItemInteger": 1200 - }] + "aaString": "abcd", + "anInteger": 90, + "aaBoolean": true, + "aaCollection": [ + { + "subItemString": "defg", + "subItemInteger": 1200 + } + ] }
\ No newline at end of file |