json schema annotation: skip property value annotation if the value does not exist

WEB-26231 JSON Schema: IllegalArgumentException when no value is set for property covered with oneOf validation
This commit is contained in:
irengrig
2017-03-29 16:13:57 +02:00
parent 0a59fb8409
commit 4caf425ec3
6 changed files with 126 additions and 27 deletions
@@ -65,6 +65,21 @@ public class JsonBySchemaObjectAnnotator implements Annotator {
}
if (checkIfAlreadyProcessed(holder, firstProp.getDelegate())) return;
final JsonValueAdapter firstPropValue = firstProp.getValue();
if (firstPropValue != null) checkFirstPropValue(element, holder, walker, firstProp, firstPropValue);
if (firstProp.getParentObject() != null && walker.isTopJsonElement(firstProp.getParentObject().getDelegate().getParent())) {
checkRootObject(holder, firstProp.getParentObject(), walker);
}
if (firstProp.getParentArray() != null && walker.isTopJsonElement(firstProp.getParentArray().getDelegate().getParent())) {
checkRootObject(holder, firstProp.getParentArray(), walker);
}
}
private void checkFirstPropValue(@NotNull PsiElement element,
@NotNull AnnotationHolder holder,
JsonLikePsiWalker walker,
JsonPropertyAdapter firstProp, JsonValueAdapter firstPropValue) {
final List<BySchemaChecker> checkers = new ArrayList<>();
JsonSchemaWalker.findSchemasForAnnotation(firstProp.getDelegate(), JsonSchemaWalker.getWalker(element, myRootSchema), new JsonSchemaWalker.CompletionSchemesConsumer() {
@Override
@@ -74,7 +89,7 @@ public class JsonBySchemaObjectAnnotator implements Annotator {
@NotNull List<JsonSchemaWalker.Step> steps) {
final BySchemaChecker checker = new BySchemaChecker(walker);
final Set<String> validatedProperties = new HashSet<>();
checker.checkByScheme(firstProp.getValue(), schema, validatedProperties);
checker.checkByScheme(firstPropValue, schema, validatedProperties);
checkers.add(checker);
}
@@ -85,7 +100,7 @@ public class JsonBySchemaObjectAnnotator implements Annotator {
@NotNull List<JsonSchemaWalker.Step> steps) {
final BySchemaChecker checker = new BySchemaChecker(walker);
final Set<String> validatedProperties = new HashSet<>();
checker.processOneOf(firstProp.getValue(), list, validatedProperties);
checker.processOneOf(firstPropValue, list, validatedProperties);
checkers.add(checker);
}
@@ -96,7 +111,7 @@ public class JsonBySchemaObjectAnnotator implements Annotator {
@NotNull List<JsonSchemaWalker.Step> steps) {
final BySchemaChecker checker = new BySchemaChecker(walker);
final Set<String> validatedProperties = new HashSet<>();
checker.processAnyOf(firstProp.getValue(), list, validatedProperties);
checker.processAnyOf(firstPropValue, list, validatedProperties);
checkers.add(checker);
}
}, myRootSchema, mySchemaFile);
@@ -118,13 +133,7 @@ public class JsonBySchemaObjectAnnotator implements Annotator {
}
}
if (processCheckerResults(holder, checker)) return;
if (firstProp.getParentObject() != null && walker.isTopJsonElement(firstProp.getParentObject().getDelegate().getParent())) {
checkRootObject(holder, firstProp.getParentObject(), walker);
}
if (firstProp.getParentArray() != null && walker.isTopJsonElement(firstProp.getParentArray().getDelegate().getParent())) {
checkRootObject(holder, firstProp.getParentArray(), walker);
}
processCheckerResults(holder, checker);
}
private static JsonValueAdapter findTopLevelElement(@NotNull JsonLikePsiWalker walker, @NotNull PsiElement element) {
@@ -160,15 +169,13 @@ public class JsonBySchemaObjectAnnotator implements Annotator {
return false;
}
private static boolean processCheckerResults(@NotNull AnnotationHolder holder, BySchemaChecker checker) {
private static void processCheckerResults(@NotNull AnnotationHolder holder, BySchemaChecker checker) {
if (! checker.isCorrect()) {
for (Map.Entry<PsiElement, String> entry : checker.getErrors().entrySet()) {
if (checkIfAlreadyProcessed(holder, entry.getKey())) continue;
holder.createWarningAnnotation(entry.getKey(), entry.getValue());
}
return true;
}
return false;
}
private static class BySchemaChecker {
@@ -604,7 +611,7 @@ public class JsonBySchemaObjectAnnotator implements Annotator {
}
}
private void processOneOf(JsonValueAdapter value, List<JsonSchemaObject> oneOf, Set<String> validatedProperties) {
private void processOneOf(@NotNull JsonValueAdapter value, List<JsonSchemaObject> oneOf, Set<String> validatedProperties) {
final Map<PsiElement, String> errors = new HashMap<>();
int cntCorrect = 0;
boolean validatedPropertiesAdded = false;
@@ -647,7 +654,7 @@ public class JsonBySchemaObjectAnnotator implements Annotator {
return !checker.getErrors().containsKey(value);
}
private void processAnyOf(JsonValueAdapter value, List<JsonSchemaObject> anyOf, Set<String> validatedProperties) {
private void processAnyOf(@NotNull JsonValueAdapter value, List<JsonSchemaObject> anyOf, Set<String> validatedProperties) {
final Map<PsiElement, String> errors = new HashMap<>();
for (JsonSchemaObject object : anyOf) {
final BySchemaChecker checker = new BySchemaChecker(myWalker);
@@ -328,6 +328,12 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase {
testImpl(schemaText, inputText);
}
public void testOneOfWithEmptyPropertyValue() throws Exception {
String schemaText = FileUtil.loadFile(new File(getTestDataPath() + "/oneOfSchema.json"));
String inputText = FileUtil.loadFile(new File(getTestDataPath() + "/oneOfWithEmptyPropertyValue.json"));
testImpl(schemaText, inputText);
}
public void testCycledSchema() throws Exception {
String schemaText = FileUtil.loadFile(new File(getTestDataPath() + "/cycledSchema.json"));
String inputText = FileUtil.loadFile(new File(getTestDataPath() + "/testCycledSchema.json"));
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
import com.intellij.codeInsight.completion.CompletionType;
import com.jetbrains.jsonSchema.JsonSchemaHeavyAbstractTest;
import com.jetbrains.jsonSchema.JsonSchemaMappingsConfigurationBase;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
@@ -85,7 +86,35 @@ public class JsonBySchemaHeavyCompletionTest extends JsonSchemaHeavyAbstractTest
baseInsertTest("insertPropertyName", "testNameWithDefaultStringValueComma");
}
private void baseInsertTest(final String folder, final String testFile) throws Exception {
public void testOneOfWithNotFilledPropertyValue() throws Exception {
baseCompletionTest("oneOfWithEnumValue", "oneOfWithEmptyPropertyValue", "\"business\"", "\"home\"");
}
private void baseCompletionTest(@SuppressWarnings("SameParameterValue") final String folder,
@SuppressWarnings("SameParameterValue") final String testFile, @NotNull String... items) throws Exception {
baseTest(folder, testFile, () -> {
complete();
assertStringItems(items);
});
}
private void baseInsertTest(@SuppressWarnings("SameParameterValue") final String folder, final String testFile) throws Exception {
baseTest(folder, testFile, () -> {
final CodeCompletionHandlerBase handlerBase = new CodeCompletionHandlerBase(CompletionType.BASIC);
handlerBase.invokeCompletion(getProject(), getEditor());
if (myItems != null) {
selectItem(myItems[0]);
}
try {
checkResultByFile("/" + folder + "/" + testFile + "_after.json");
}
catch (Exception e) {
throw new RuntimeException(e);
}
});
}
private void baseTest(@NotNull final String folder, @NotNull final String testFile, @NotNull final Runnable checker) throws Exception {
skeleton(new Callback() {
@Override
public void registerSchemes() {
@@ -105,17 +134,7 @@ public class JsonBySchemaHeavyCompletionTest extends JsonSchemaHeavyAbstractTest
@Override
public void doCheck() {
final CodeCompletionHandlerBase handlerBase = new CodeCompletionHandlerBase(CompletionType.BASIC);
handlerBase.invokeCompletion(getProject(), getEditor());
if (myItems != null) {
selectItem(myItems[0]);
}
try {
checkResultByFile("/" + folder + "/" + testFile + "_after.json");
}
catch (Exception e) {
throw new RuntimeException(e);
}
checker.run();
}
});
}
@@ -0,0 +1,55 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
},
"office_address": {
"properties": {
"type": { "enum": [ "business" ] },
"building": { "type": "string" }
},
"required": ["type", "building"],
"additionalProperties": false
},
"home_address": {
"properties": {
"type": { "enum": [ "home" ] },
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["type", "street_address", "city", "state"],
"additionalProperties": false
},
"partOne": {
"properties": {"one": {"type": "string"}}
},
"partTwo": {
"properties": {"two": {"type": "integer"}}
}
},
"type": "object",
"properties": {
"client_address": {
"oneOf": [
{ "$ref": "#/definitions/office_address" },
{ "$ref": "#/definitions/home_address" }
]
},
"parts": {
"oneOf": [
{ "$ref": "#/definitions/partOne" },
{ "$ref": "#/definitions/partTwo" }
],
"additionalProperties": false
}
}
}
@@ -0,0 +1,6 @@
{
"client_address": {
"building": "1",
"type":<caret>
}
}
@@ -0,0 +1,6 @@
{
"client_address": {
"building": "1",
"type":<EOLError descr="<value> expected, got '}'"></EOLError>
}
}