• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Network New Technologies Inc.
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 com.fasterxml.jackson.core.JsonProcessingException;
20 import com.fasterxml.jackson.databind.JsonNode;
21 import com.fasterxml.jackson.databind.ObjectMapper;
22 import org.junit.jupiter.api.Test;
23 
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Set;
29 
30 import static org.junit.jupiter.api.Assertions.assertEquals;
31 
32 public class CustomMetaSchemaTest {
33 
34     /**
35      * Introduces the keyword "enumNames".
36      * <p>
37      * The keyword is used together with "enum" and must have the same length.
38      * <p>
39      * This keyword always produces a warning during validation -
40      * so it makes no sense in reality but should be useful for demonstration / testing purposes.
41      *
42      * @author klaskalass
43      */
44     public static class EnumNamesKeyword extends AbstractKeyword {
45 
46         private static final class Validator extends AbstractJsonValidator {
47             private final List<String> enumValues;
48             private final List<String> enumNames;
49             private final String keyword;
50 
Validator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, String keyword, List<String> enumValues, List<String> enumNames, JsonNode schemaNode)51             private Validator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, String keyword,
52                     List<String> enumValues, List<String> enumNames, JsonNode schemaNode) {
53                 super(schemaLocation, evaluationPath, new EnumNamesKeyword(), schemaNode);
54                 if (enumNames.size() != enumValues.size()) {
55                     throw new IllegalArgumentException("enum and enumNames need to be of same length");
56                 }
57                 this.enumNames = enumNames;
58                 this.enumValues = enumValues;
59                 this.keyword = keyword;
60             }
61 
62             @Override
validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation)63             public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation) {
64                 String value = node.asText();
65                 int idx = enumValues.indexOf(value);
66                 if (idx < 0) {
67                     throw new IllegalArgumentException("value not found in enum. value: " + value + " enum: " + enumValues);
68                 }
69                 String valueName = enumNames.get(idx);
70                 Set<ValidationMessage> messages = new HashSet<>();
71                 ValidationMessage validationMessage = ValidationMessage.builder().type(keyword)
72                         .schemaNode(node)
73                         .instanceNode(node)
74                         .code("tests.example.enumNames").message("{0}: enumName is {1}").instanceLocation(instanceLocation)
75                         .arguments(valueName).build();
76                 messages.add(validationMessage);
77                 return messages;
78             }
79         }
80 
81 
EnumNamesKeyword()82         public EnumNamesKeyword() {
83             super("enumNames");
84         }
85 
86         @Override
newValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext)87         public JsonValidator newValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode,
88                                           JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception {
89             /*
90              * You can access the schema node here to read data from your keyword
91              */
92             if (!schemaNode.isArray()) {
93                 throw new JsonSchemaException("Keyword enumNames needs to receive an array");
94             }
95             JsonNode parentSchemaNode = parentSchema.getSchemaNode();
96             if (!parentSchemaNode.has("enum")) {
97                 throw new JsonSchemaException("Keyword enumNames needs to have a sibling enum keyword");
98             }
99             JsonNode enumSchemaNode = parentSchemaNode.get("enum");
100 
101             return new Validator(schemaLocation, evaluationPath, getValue(), readStringList(enumSchemaNode),
102                     readStringList(schemaNode), schemaNode);
103         }
104 
readStringList(JsonNode node)105         private List<String> readStringList(JsonNode node) {
106             if (!node.isArray()) {
107                 throw new JsonSchemaException("Keyword enum needs to receive an array");
108             }
109             ArrayList<String> result = new ArrayList<String>(node.size());
110             for (JsonNode child : node) {
111                 result.add(child.asText());
112             }
113             return result;
114         }
115     }
116 
117     @Test
customMetaSchemaWithIgnoredKeyword()118     public void customMetaSchemaWithIgnoredKeyword() throws JsonProcessingException, IOException {
119         ObjectMapper objectMapper = new ObjectMapper();
120         final JsonMetaSchema metaSchema = JsonMetaSchema
121                 .builder("https://github.com/networknt/json-schema-validator/tests/schemas/example01", JsonMetaSchema.getV4())
122                 // Generated UI uses enumNames to render Labels for enum values
123                 .keyword(new EnumNamesKeyword())
124                 .build();
125 
126         final JsonSchemaFactory validatorFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).metaSchema(metaSchema).build();
127         final JsonSchema schema = validatorFactory.getSchema("{\n" +
128                 "  \"$schema\":\n" +
129                 "    \"https://github.com/networknt/json-schema-validator/tests/schemas/example01\",\n" +
130                 "  \"enum\": [\"foo\", \"bar\"],\n" +
131                 "  \"enumNames\": [\"Foo !\", \"Bar !\"]\n" +
132                 "}");
133 
134         Set<ValidationMessage> messages = schema.validate(objectMapper.readTree("\"foo\""));
135         assertEquals(1, messages.size());
136 
137         ValidationMessage message = messages.iterator().next();
138         assertEquals("$: enumName is Foo !", message.getMessage());
139     }
140 }
141