diff --git a/build.gradle b/build.gradle index 728760228..d1cbb35fb 100644 --- a/build.gradle +++ b/build.gradle @@ -190,7 +190,6 @@ dependencies { implementation "org.opensearch:opensearch:${opensearch_version}" implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" implementation "org.jetbrains.kotlin:kotlin-stdlib-common:${kotlin_version}" - implementation "org.jetbrains.kotlin:kotlin-reflect:${kotlin_version}" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" implementation "${group}:common-utils:${common_utils_version}" implementation group: 'com.google.guava', name: 'guava', version: '31.0.1-jre' @@ -199,12 +198,13 @@ dependencies { // json-base, jackson-databind, jackson-annotations are only used by json-flattener. // see https://github.com/opensearch-project/OpenSearch/issues/5395. implementation group: 'com.github.wnameless.json', name: 'json-base', version: '2.2.1' - implementation "com.fasterxml.jackson.core:jackson-core:${jackson_version}" implementation "com.fasterxml.jackson.core:jackson-databind:${jackson_version}" implementation "com.fasterxml.jackson.core:jackson-annotations:${jackson_version}" - implementation "com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}" - implementation group: 'com.worldturner.medeia', name: 'medeia-validator-core', version: '1.1.1' - implementation group: 'com.worldturner.medeia', name: 'medeia-validator-jackson', version: '1.1.1' + // Forced dependency from json-schema-validator, we add nop to deactivate it + implementation group: 'org.slf4j', name: 'slf4j-api', version: '2.0.6' + implementation group: 'org.slf4j', name: 'slf4j-nop', version: '2.0.6' + implementation group: 'com.ethlo.time', name: 'itu', version: '1.7.0' + implementation group: 'com.networknt', name: 'json-schema-validator', version: '1.0.78' compileOnly "${group}:opensearch-job-scheduler-spi:${job_scheduler_version}" testImplementation( 'org.assertj:assertj-core:3.16.1', diff --git a/src/main/kotlin/org/opensearch/observability/validation/Validator.kt b/src/main/kotlin/org/opensearch/observability/validation/Validator.kt index 1468d0b19..ceba942e0 100644 --- a/src/main/kotlin/org/opensearch/observability/validation/Validator.kt +++ b/src/main/kotlin/org/opensearch/observability/validation/Validator.kt @@ -1,16 +1,10 @@ package org.opensearch.observability.validation -import com.fasterxml.jackson.core.JsonParseException -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.worldturner.medeia.api.PathSchemaSource -import com.worldturner.medeia.api.ValidationFailedException -import com.worldturner.medeia.api.jackson.MedeiaJacksonApi -import com.worldturner.medeia.schema.validation.SchemaValidator -import org.opensearch.common.xcontent.DeprecationHandler -import org.opensearch.common.xcontent.NamedXContentRegistry -import org.opensearch.common.xcontent.XContentParser -import org.opensearch.common.xcontent.json.JsonXContent +import com.fasterxml.jackson.databind.ObjectMapper +import com.networknt.schema.JsonSchema +import com.networknt.schema.JsonSchemaFactory +import com.networknt.schema.SpecVersionDetector +import com.networknt.schema.ValidationResult import org.opensearch.commons.utils.logger import java.io.File import java.io.FileNotFoundException @@ -20,37 +14,23 @@ class Validator(val component: IntegrationComponent) { private val log by logger(Validator::class.java) } - private val medeiaApi = MedeiaJacksonApi() + private val mapper = ObjectMapper() - private fun loadComponentSchema(): SchemaValidator { + private fun loadComponentSchema(): JsonSchema { val schemaFile = File(component.resourcePath) if (!schemaFile.exists()) { log.fatal("could not find schema '${schemaFile.path}' for component '$component'") throw FileNotFoundException("could not find schema '${schemaFile.path}'") } - val schemaSource = PathSchemaSource(schemaFile.toPath()) - return medeiaApi.loadSchema(schemaSource) + val schemaSource = schemaFile.readText(Charsets.UTF_8) + val schemaNode = mapper.readTree(schemaSource) + val factory = JsonSchemaFactory.getInstance(SpecVersionDetector.detect(schemaNode)) + return factory.getSchema(schemaNode) } - fun validate(json: String): Result { - return try { - val mapper = jacksonObjectMapper() - val medeia = medeiaApi.decorateJsonParser( - loadComponentSchema(), - mapper.createParser(json) - ) - // Validation happens when tree is read - medeia.readValueAsTree() - val xContentParser = JsonXContent.jsonXContent.createParser( - NamedXContentRegistry.EMPTY, - DeprecationHandler.THROW_UNSUPPORTED_OPERATION, - json - ) - Result.success(xContentParser) - } catch (ex: JsonParseException) { - Result.failure(ex) - } catch (ex: ValidationFailedException) { - Result.failure(ex) - } + fun validate(json: String): ValidationResult { + val schema = loadComponentSchema() + val node = mapper.readTree(json) + return schema.validateAndCollect(node) } } diff --git a/src/test/kotlin/org/opensearch/observability/validation/ValidatorTests.kt b/src/test/kotlin/org/opensearch/observability/validation/ValidatorTests.kt index 4fab4ec8d..d266642c0 100644 --- a/src/test/kotlin/org/opensearch/observability/validation/ValidatorTests.kt +++ b/src/test/kotlin/org/opensearch/observability/validation/ValidatorTests.kt @@ -1,12 +1,12 @@ package org.opensearch.observability.validation import com.fasterxml.jackson.core.JsonParseException -import com.worldturner.medeia.api.ValidationFailedException import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.opensearch.common.Strings import org.opensearch.common.xcontent.json.JsonXContent import java.io.File +import kotlin.test.assertEquals internal class ValidatorTests { private fun buildIntegration(with: Map = mapOf(), without: Set = setOf()): String { @@ -44,7 +44,7 @@ internal class ValidatorTests { val config = "{" val validator = Validator(IntegrationComponent.INTEGRATION) assertThrows { - validator.validate(config).getOrThrow() + validator.validate(config) } } @@ -65,7 +65,7 @@ internal class ValidatorTests { val sampleDir = component.resourcePath.substring(0, component.resourcePath.lastIndexOf("/")) + "/samples/" val samplePath = sampleDir + sampleFiles[component] val sampleJson = File(samplePath).readText(Charsets.UTF_8) - validator.validate(sampleJson).getOrThrow() + assertEquals(validator.validate(sampleJson).validationMessages.size, 0) } } @@ -73,17 +73,13 @@ internal class ValidatorTests { fun testValidatorMissingField() { val config = buildIntegration(without = setOf("name")) val validator = Validator(IntegrationComponent.INTEGRATION) - assertThrows { - validator.validate(config).getOrThrow() - } + assertEquals(validator.validate(config).validationMessages.size, 1) } @Test fun testValidatorWrongFieldType() { val config = buildIntegration(mapOf(Pair("name", 1))) val validator = Validator(IntegrationComponent.INTEGRATION) - assertThrows { - validator.validate(config).getOrThrow() - } + assertEquals(validator.validate(config).validationMessages.size, 1) } }