WEB-34103 JSON schema validation for enums of arrays/objects giving incorrect warning

This commit is contained in:
Anton Lobov
2018-07-31 12:43:35 +02:00
parent 23906974df
commit db6ed8d66a
5 changed files with 136 additions and 7 deletions
@@ -0,0 +1,25 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.jsonSchema.impl;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.stream.Collectors;
public class EnumArrayValueWrapper {
@NotNull private final Object[] myValues;
public EnumArrayValueWrapper(@NotNull Object[] values) {
myValues = values;
}
@NotNull
public Object[] getValues() {
return myValues;
}
@Override
public String toString() {
return "[" + Arrays.stream(myValues).map(v -> v.toString()).collect(Collectors.joining(", ")) + "]";
}
}
@@ -0,0 +1,25 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.jsonSchema.impl;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.stream.Collectors;
public class EnumObjectValueWrapper {
@NotNull private final Map<String, Object> myValues;
public EnumObjectValueWrapper(@NotNull Map<String, Object> values) {
myValues = values;
}
@NotNull
public Map<String, Object> getValues() {
return myValues;
}
@Override
public String toString() {
return "{" + myValues.entrySet().stream().map(v -> "\"" + v.getKey() + "\": " + v.getValue()).collect(Collectors.joining(", ")) + "}";
}
}
@@ -456,6 +456,51 @@ class JsonSchemaAnnotatorChecker {
return position;
}
private static boolean checkEnumValue(@NotNull Object object,
@NotNull JsonLikePsiWalker walker,
@Nullable JsonValueAdapter adapter,
@NotNull String text,
@NotNull BiFunction<String, String, Boolean> stringEq) {
if (object instanceof EnumArrayValueWrapper) {
if (adapter instanceof JsonArrayValueAdapter) {
List<JsonValueAdapter> elements = ((JsonArrayValueAdapter)adapter).getElements();
Object[] values = ((EnumArrayValueWrapper)object).getValues();
if (elements.size() == values.length) {
for (int i = 0; i < values.length; i++) {
if (!checkEnumValue(values[i], walker, elements.get(i), walker.getNodeTextForValidation(elements.get(i).getDelegate()), stringEq)) return false;
}
return true;
}
}
}
else if (object instanceof EnumObjectValueWrapper) {
if (adapter instanceof JsonObjectValueAdapter) {
List<JsonPropertyAdapter> props = ((JsonObjectValueAdapter)adapter).getPropertyList();
Map<String, Object> values = ((EnumObjectValueWrapper)object).getValues();
if (props.size() == values.size()) {
for (JsonPropertyAdapter prop : props) {
if (!values.containsKey(prop.getName())) return false;
JsonValueAdapter value = prop.getValue();
if (value == null) continue;
if (!checkEnumValue(values.get(prop.getName()), walker, value, walker.getNodeTextForValidation(value.getDelegate()), stringEq)) return false;
}
return true;
}
}
}
else {
if (walker.onlyDoubleQuotesForStringLiterals()) {
if (stringEq.apply(object.toString(), text)) return true;
}
else {
if (equalsIgnoreQuotes(object.toString(), text, walker.quotesForStringLiterals(), stringEq)) return true;
}
}
return false;
}
private void checkForEnum(PsiElement value, JsonSchemaObject schema) {
//enum values + pattern -> don't check enum values
if (schema.getEnum() == null || schema.getPattern() != null) return;
@@ -465,12 +510,7 @@ class JsonSchemaAnnotatorChecker {
final List<Object> objects = schema.getEnum();
BiFunction<String, String, Boolean> eq = myOptions.isCaseInsensitiveEnumCheck() ? String::equalsIgnoreCase : String::equals;
for (Object object : objects) {
if (walker.onlyDoubleQuotesForStringLiterals()) {
if (eq.apply(object.toString(), text)) return;
}
else {
if (equalsIgnoreQuotes(object.toString(), text, walker.quotesForStringLiterals(), eq)) return;
}
if (checkEnumValue(object, walker, walker.createValueAdapter(value), text, eq)) return;
}
error("Value should be one of: [" + StringUtil.join(objects, o -> o.toString(), ", ") + "]", value,
JsonValidationError.FixableIssueKind.NonEnumValue, null, JsonErrorPriority.MEDIUM_PRIORITY);
@@ -5,6 +5,7 @@ import com.intellij.json.psi.*;
import com.intellij.notification.NotificationGroup;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -297,6 +298,13 @@ public class JsonSchemaReader {
return ((JsonBooleanLiteral)value).getValue();
} else if (value instanceof JsonNullLiteral) {
return "null";
} else if (value instanceof JsonArray) {
return new EnumArrayValueWrapper(((JsonArray)value).getValueList().stream().map(v -> readEnumValue(v)).filter(v -> v != null).toArray());
} else if (value instanceof JsonObject) {
return new EnumObjectValueWrapper(((JsonObject)value).getPropertyList().stream()
.map(p -> Pair.create(p.getName(), readEnumValue(p.getValue())))
.filter(p -> p.second != null)
.collect(Collectors.toMap(p -> p.first, p -> p.second)));
}
return null;
}
@@ -308,7 +316,8 @@ public class JsonSchemaReader {
final List<JsonValue> list = ((JsonArray)element).getValueList();
for (JsonValue value : list) {
Object enumValue = readEnumValue(value);
if (enumValue != null) objects.add(enumValue);
if (enumValue == null) return; // don't validate if we have unsupported entity kinds
objects.add(enumValue);
}
object.setEnum(objects);
}
@@ -891,4 +891,34 @@ public class JsonSchemaHighlightingTest extends JsonSchemaHighlightingTestBase {
" \"name\": <warning>\"aA\"</warning>\n" +
"}");
}
public void testEnumArrayValue() throws Exception {
@Language("JSON") String schema = "{\n" +
" \"properties\": {\n" +
" \"foo\": {\n" +
" \"enum\": [ [{\"x\": 5}, [true], \"q\"] ]\n" +
" }\n" +
" }\n" +
"}";
doTest(schema, "{\"foo\": <warning>5</warning>}");
doTest(schema, "{\"foo\": <warning>[ ]</warning>}");
doTest(schema, "{\"foo\": <warning>[{\"x\": 5}]</warning>}");
doTest(schema, "{\"foo\": <warning>[{\"x\": 5}, true]</warning>}");
doTest(schema, "{\"foo\": <warning>[{\"x\": 5}, [true]]</warning>}");
doTest(schema, "{\"foo\": [ { \"x\" : 5 } , [ true ] , \"q\" ]}");
}
public void testEnumObjectValue() throws Exception {
@Language("JSON") String schema = "{\n" +
" \"properties\": {\n" +
" \"foo\": {\n" +
" \"enum\": [ {\"x\": 5} ]\n" +
" }\n" +
" }\n" +
"}";
doTest(schema, "{\"foo\": <warning>{}</warning>}");
doTest(schema, "{\"foo\": <warning>{\"x\": 4}</warning>}");
doTest(schema, "{\"foo\": <warning>{\"x\": true}</warning>}");
doTest(schema, "{\"foo\": { \r \"x\" : \t 5 \n }}");
}
}