1 /* 2 * Copyright (c) 2024 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.networknt.schema; 18 19 import static org.junit.jupiter.api.Assertions.assertEquals; 20 import static org.junit.jupiter.api.Assertions.assertNotNull; 21 import static org.junit.jupiter.api.Assertions.assertThrows; 22 23 import java.util.Collections; 24 import java.util.Set; 25 26 import org.junit.jupiter.api.Test; 27 28 import com.networknt.schema.SpecVersion.VersionFlag; 29 import com.networknt.schema.output.OutputUnit; 30 31 /** 32 * Tests for vocabulary support in meta schemas. 33 */ 34 public class VocabularyTest { 35 @Test noValidation()36 void noValidation() { 37 String metaSchemaData = "{\r\n" 38 + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\r\n" 39 + " \"$id\": \"https://www.example.com/no-validation-no-format/schema\",\r\n" 40 + " \"$vocabulary\": {\r\n" 41 + " \"https://www.example.com/vocab/validation\": false,\r\n" 42 + " \"https://json-schema.org/draft/2020-12/vocab/applicator\": true,\r\n" 43 + " \"https://json-schema.org/draft/2020-12/vocab/core\": true\r\n" 44 + " },\r\n" 45 + " \"allOf\": [\r\n" 46 + " { \"$ref\": \"https://json-schema.org/draft/2020-12/meta/applicator\" },\r\n" 47 + " { \"$ref\": \"https://json-schema.org/draft/2020-12/meta/core\" }\r\n" 48 + " ]\r\n" 49 + "}"; 50 String schemaData = "{\r\n" 51 + " \"$id\": \"https://schema/using/no/validation\",\r\n" 52 + " \"$schema\": \"https://www.example.com/no-validation-no-format/schema\",\r\n" 53 + " \"properties\": {\r\n" 54 + " \"badProperty\": false,\r\n" 55 + " \"numberProperty\": {\r\n" 56 + " \"minimum\": 10\r\n" 57 + " }\r\n" 58 + " }\r\n" 59 + "}"; 60 JsonSchema schema = JsonSchemaFactory 61 .getInstance(VersionFlag.V202012, 62 builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(Collections 63 .singletonMap("https://www.example.com/no-validation-no-format/schema", 64 metaSchemaData)))) 65 .getSchema(schemaData); 66 67 String inputDataNoValidation = "{\r\n" 68 + " \"numberProperty\": 1\r\n" 69 + "}"; 70 71 Set<ValidationMessage> messages = schema.validate(inputDataNoValidation, InputFormat.JSON); 72 assertEquals(0, messages.size()); 73 74 // Set validation vocab 75 schema = JsonSchemaFactory 76 .getInstance(VersionFlag.V202012, 77 builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(Collections 78 .singletonMap("https://www.example.com/no-validation-no-format/schema", 79 metaSchemaData.replace("https://www.example.com/vocab/validation", 80 Vocabulary.V202012_VALIDATION.getIri()))))) 81 .getSchema(schemaData); 82 messages = schema.validate(inputDataNoValidation, InputFormat.JSON); 83 assertEquals(1, messages.size()); 84 assertEquals("minimum", messages.iterator().next().getType()); 85 } 86 87 @Test noFormatValidation()88 void noFormatValidation() { 89 String metaSchemaData = "{\r\n" 90 + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\r\n" 91 + " \"$id\": \"https://www.example.com/no-validation-no-format/schema\",\r\n" 92 + " \"$vocabulary\": {\r\n" 93 + " \"https://www.example.com/vocab/format\": false,\r\n" 94 + " \"https://json-schema.org/draft/2020-12/vocab/applicator\": true,\r\n" 95 + " \"https://json-schema.org/draft/2020-12/vocab/core\": true\r\n" 96 + " },\r\n" 97 + " \"allOf\": [\r\n" 98 + " { \"$ref\": \"https://json-schema.org/draft/2020-12/meta/applicator\" },\r\n" 99 + " { \"$ref\": \"https://json-schema.org/draft/2020-12/meta/core\" }\r\n" 100 + " ]\r\n" 101 + "}"; 102 String schemaData = "{\r\n" 103 + " \"$id\": \"https://schema/using/no/format\",\r\n" 104 + " \"$schema\": \"https://www.example.com/no-validation-no-format/schema\",\r\n" 105 + " \"properties\": {\r\n" 106 + " \"dateProperty\": {\r\n" 107 + " \"format\": \"date\"\r\n" 108 + " }\r\n" 109 + " }\r\n" 110 + "}"; 111 JsonSchema schema = JsonSchemaFactory 112 .getInstance(VersionFlag.V202012, 113 builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(Collections 114 .singletonMap("https://www.example.com/no-validation-no-format/schema", 115 metaSchemaData)))) 116 .getSchema(schemaData); 117 118 String inputDataNoValidation = "{\r\n" 119 + " \"dateProperty\": \"hello\"\r\n" 120 + "}"; 121 122 Set<ValidationMessage> messages = schema.validate(inputDataNoValidation, InputFormat.JSON, 123 executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); 124 assertEquals(0, messages.size()); 125 126 // Set format assertion vocab 127 schema = JsonSchemaFactory 128 .getInstance(VersionFlag.V202012, 129 builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(Collections 130 .singletonMap("https://www.example.com/no-validation-no-format/schema", 131 metaSchemaData.replace("https://www.example.com/vocab/format", 132 Vocabulary.V202012_FORMAT_ASSERTION.getIri()))))) 133 .getSchema(schemaData); 134 messages = schema.validate(inputDataNoValidation, InputFormat.JSON); 135 assertEquals(1, messages.size()); 136 assertEquals("format", messages.iterator().next().getType()); 137 } 138 139 @Test requiredUnknownVocabulary()140 void requiredUnknownVocabulary() { 141 String metaSchemaData = "{\r\n" 142 + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\r\n" 143 + " \"$id\": \"https://www.example.com/no-validation-no-format/schema\",\r\n" 144 + " \"$vocabulary\": {\r\n" 145 + " \"https://www.example.com/vocab/format\": true,\r\n" 146 + " \"https://json-schema.org/draft/2020-12/vocab/applicator\": true,\r\n" 147 + " \"https://json-schema.org/draft/2020-12/vocab/core\": true\r\n" 148 + " },\r\n" 149 + " \"allOf\": [\r\n" 150 + " { \"$ref\": \"https://json-schema.org/draft/2020-12/meta/applicator\" },\r\n" 151 + " { \"$ref\": \"https://json-schema.org/draft/2020-12/meta/core\" }\r\n" 152 + " ]\r\n" 153 + "}"; 154 String schemaData = "{\r\n" 155 + " \"$id\": \"https://schema/using/no/format\",\r\n" 156 + " \"$schema\": \"https://www.example.com/no-validation-no-format/schema\",\r\n" 157 + " \"properties\": {\r\n" 158 + " \"dateProperty\": {\r\n" 159 + " \"format\": \"date\"\r\n" 160 + " }\r\n" 161 + " }\r\n" 162 + "}"; 163 JsonSchemaFactory factory = JsonSchemaFactory 164 .getInstance(VersionFlag.V202012, 165 builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(Collections 166 .singletonMap("https://www.example.com/no-validation-no-format/schema", 167 metaSchemaData)))); 168 assertThrows(InvalidSchemaException.class, () -> factory.getSchema(schemaData)); 169 } 170 171 @Test customVocabulary()172 void customVocabulary() { 173 String metaSchemaData = "{\r\n" 174 + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\r\n" 175 + " \"$id\": \"https://www.example.com/no-validation-no-format/schema\",\r\n" 176 + " \"$vocabulary\": {\r\n" 177 + " \"https://www.example.com/vocab/format\": true,\r\n" 178 + " \"https://json-schema.org/draft/2020-12/vocab/applicator\": true,\r\n" 179 + " \"https://json-schema.org/draft/2020-12/vocab/core\": true\r\n" 180 + " },\r\n" 181 + " \"allOf\": [\r\n" 182 + " { \"$ref\": \"https://json-schema.org/draft/2020-12/meta/applicator\" },\r\n" 183 + " { \"$ref\": \"https://json-schema.org/draft/2020-12/meta/core\" }\r\n" 184 + " ]\r\n" 185 + "}"; 186 String schemaData = "{\r\n" 187 + " \"$id\": \"https://schema/using/no/format\",\r\n" 188 + " \"$schema\": \"https://www.example.com/no-validation-no-format/schema\",\r\n" 189 + " \"hello\": {\r\n" 190 + " \"dateProperty\": {\r\n" 191 + " \"format\": \"date\"\r\n" 192 + " }\r\n" 193 + " }\r\n" 194 + "}"; 195 VocabularyFactory vocabularyFactory = uri -> { 196 if ("https://www.example.com/vocab/format".equals(uri)) { 197 return new Vocabulary("https://www.example.com/vocab/format", new AnnotationKeyword("hello")); 198 } 199 return null; 200 }; 201 202 JsonMetaSchema metaSchema = JsonMetaSchema 203 .builder(JsonMetaSchema.getV202012().getIri(), JsonMetaSchema.getV202012()) 204 .vocabularyFactory(vocabularyFactory) 205 .build(); 206 JsonSchemaFactory factory = JsonSchemaFactory 207 .getInstance(VersionFlag.V202012, 208 builder -> builder.metaSchema(metaSchema).schemaLoaders(schemaLoaders -> schemaLoaders.schemas(Collections 209 .singletonMap("https://www.example.com/no-validation-no-format/schema", 210 metaSchemaData)))); 211 JsonSchema schema = factory.getSchema(schemaData); 212 OutputUnit outputUnit = schema.validate("{}", InputFormat.JSON, OutputFormat.HIERARCHICAL, executionContext -> { 213 executionContext.getExecutionConfig().setAnnotationCollectionEnabled(true); 214 executionContext.getExecutionConfig().setAnnotationCollectionFilter(keyword -> true); 215 }); 216 assertNotNull(outputUnit.getAnnotations().get("hello")); 217 } 218 } 219