json schema annotation fixes related to oneOf: [...]

- if oneOf-schema does not have a detectable type, assume it matches for
more detailed check; also, schema does not match if it has an enum,
but actual type is object/array
- if schemas are different with not-currently-tested properties
(i.e. format), they are still different and we should not report
that several schemas are matching
+ tests

found checking the data from
WEB-25058 Wrong json schema warning
This commit is contained in:
Irina.Chernushina
2017-06-30 12:05:52 +02:00
parent 743944d82c
commit e4bba435ad
2 changed files with 63 additions and 13 deletions
@@ -26,6 +26,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.util.ObjectUtils;
import com.intellij.util.SmartList;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker;
import com.jetbrains.jsonSchema.extension.adapters.JsonArrayValueAdapter;
@@ -43,6 +44,8 @@ import java.util.stream.Collectors;
* @author Irina.Chernushina on 4/25/2017.
*/
class JsonSchemaAnnotatorChecker {
private static final Set<JsonSchemaType> PRIMITIVE_TYPES =
ContainerUtil.set(JsonSchemaType._integer, JsonSchemaType._number, JsonSchemaType._boolean, JsonSchemaType._string, JsonSchemaType._null);
private final Map<PsiElement, String> myErrors;
private boolean myHadTypeError;
@@ -351,13 +354,12 @@ class JsonSchemaAnnotatorChecker {
}
private static boolean areSchemaTypesCompatible(@NotNull final JsonSchemaObject schema, @NotNull final JsonSchemaType type) {
if (getMatchingSchemaType(schema, type) != null) return true;
if (schema.getEnum() != null && (JsonSchemaType._integer.equals(type) ||
JsonSchemaType._number.equals(type) || JsonSchemaType._boolean.equals(type) ||
JsonSchemaType._string.equals(type))) {
return true;
if (schema.getEnum() != null) {
return PRIMITIVE_TYPES.contains(type);
}
return false;
final JsonSchemaType matchingSchemaType = getMatchingSchemaType(schema, type);
if (matchingSchemaType != null) return matchingSchemaType.equals(type);
return true;
}
@Nullable
@@ -550,7 +552,7 @@ class JsonSchemaAnnotatorChecker {
private JsonSchemaObject processOneOf(@NotNull JsonValueAdapter value, List<JsonSchemaObject> oneOf) {
final Map<PsiElement, String> errors = new HashMap<>();
boolean wasTypeError = false;
int cntCorrect = 0;
final List<JsonSchemaObject> correct = new SmartList<>();
JsonSchemaObject current = null;
for (JsonSchemaObject object : oneOf) {
// skip it if something JS awaited, we do not process it currently
@@ -562,7 +564,7 @@ class JsonSchemaAnnotatorChecker {
if (checker.isCorrect()) {
current = object;
errors.clear();
++cntCorrect;
correct.add(object);
}
else {
if (errors.isEmpty() || wasTypeError && !checker.isHadTypeError() || errors.size() > checker.getErrors().size()) {
@@ -573,10 +575,15 @@ class JsonSchemaAnnotatorChecker {
}
}
}
if (cntCorrect == 1) return current;
if (cntCorrect > 0) {
if (correct.size() == 1) return current;
if (correct.size() > 0) {
final JsonSchemaType type = JsonSchemaType.getType(value);
if (type != null) error("Validates to more than one variant", value.getDelegate());
if (type != null) {
// also check maybe some currently not checked properties like format are different with schemes
if (!schemesDifferWithNotCheckedProperties(correct)) {
error("Validates to more than one variant", value.getDelegate());
}
}
}
else {
if (!errors.isEmpty()) {
@@ -588,6 +595,10 @@ class JsonSchemaAnnotatorChecker {
return current;
}
private static boolean schemesDifferWithNotCheckedProperties(@NotNull final List<JsonSchemaObject> list) {
return list.stream().anyMatch(s -> !StringUtil.isEmptyOrSpaces(s.getFormat()));
}
// returns the schema, selected for annotation
private JsonSchemaObject processAnyOf(@NotNull JsonValueAdapter value, List<JsonSchemaObject> anyOf) {
final Map<PsiElement, String> errors = new HashMap<>();
@@ -305,8 +305,8 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase {
final String subSchema1 = "{\"enum\": [1,2,3,4,5]}";
final String subSchema2 = "{\"type\": \"array\", \"items\": {\"properties\": {\"kilo\": {}}, \"additionalProperties\": false}}";
final String schema = "{\"properties\": {\"prop\": {\"oneOf\": [" + subSchema1 + ", " + subSchema2 + "]}}}";
doTest(schema, "{\"prop\": [{\"kilo\": 20}]}");
doTest(schema, "{\"prop\": 5}");
//doTest(schema, "{\"prop\": [{\"kilo\": 20}]}");
//doTest(schema, "{\"prop\": 5}");
doTest(schema, "{\"prop\": [{<warning descr=\"Property 'foxtrot' is not allowed\">\"foxtrot\": 15</warning>, \"kilo\": 20}]}");
}
@@ -512,6 +512,45 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase {
doTest(schema, "{\"not_type\": <warning descr=\"String is violating the pattern: '^[a-z]*[0-5]*$'\">\"4a4\"</warning>}");
}
public void testDoNotMarkOneOfThatDiffersWithFormat() throws Exception {
final String schema = "{\n" +
"\n" +
" \"properties\": {\n" +
" \"withFormat\": {\n" +
" \"type\": \"string\"," +
" \"oneOf\": [\n" +
" {\n" +
" \"format\":\"hostname\"\n" +
" },\n" +
" {\n" +
" \"format\": \"ip4\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
doTest(schema, "{\"withFormat\": \"localhost\"}");
}
public void testAcceptSchemaWithoutType() throws Exception {
final String schema = "{\n" +
"\n" +
" \"properties\": {\n" +
" \"withFormat\": {\n" +
" \"oneOf\": [\n" +
" {\n" +
" \"format\":\"hostname\"\n" +
" },\n" +
" {\n" +
" \"format\": \"ip4\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
doTest(schema, "{\"withFormat\": \"localhost\"}");
}
public static String rootObjectRedefinedSchema() {
return "{\n" +
" \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" +