You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2016/03/22 13:11:12 UTC
lucene-solr:apiv2: SOLR-8029: SchemaValidation
Repository: lucene-solr
Updated Branches:
refs/heads/apiv2 376c4e853 -> c7f58b820
SOLR-8029: SchemaValidation
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/c7f58b82
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/c7f58b82
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/c7f58b82
Branch: refs/heads/apiv2
Commit: c7f58b8205df8bcf21d2b7c83b77e3a324dca97d
Parents: 376c4e8
Author: Noble Paul <no...@apache.org>
Authored: Tue Mar 22 17:40:48 2016 +0530
Committer: Noble Paul <no...@apache.org>
Committed: Tue Mar 22 17:40:48 2016 +0530
----------------------------------------------------------------------
.../apache/solr/util/JsonSchemaValidator.java | 158 +++++++++++++++++++
...cluster.security.RuleBasedAuthorization.json | 26 ++-
.../resources/apispec/collections.Commands.json | 22 ++-
.../collections.collection.Commands.modify.json | 11 +-
.../collections.collection.Commands.reload.json | 1 +
.../collections.collection.shards.Commands.json | 2 +-
...ctions.collection.shards.shard.Commands.json | 3 +
.../apispec/core.SchemaEdit.addCopyField.json | 5 +-
.../apispec/core.SchemaEdit.addFieldType.json | 2 +-
.../core.SchemaEdit.deleteCopyField.json | 3 +-
.../core.SchemaEdit.deleteDynamicField.json | 1 +
.../core.SchemaEdit.deleteFieldType.json | 1 +
.../resources/apispec/core.config.Commands.json | 11 +-
.../apispec/core.config.Params.Commands.json | 5 +-
.../src/resources/apispec/cores.Commands.json | 3 +
.../resources/apispec/cores.core.Commands.json | 16 +-
.../apispec/cores.core.Commands.split.json | 13 +-
.../org/apache/solr/util/JsonValidatorTest.java | 55 +++++++
18 files changed, 310 insertions(+), 28 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/java/org/apache/solr/util/JsonSchemaValidator.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/util/JsonSchemaValidator.java b/solr/core/src/java/org/apache/solr/util/JsonSchemaValidator.java
new file mode 100644
index 0000000..723f1840
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/util/JsonSchemaValidator.java
@@ -0,0 +1,158 @@
+package org.apache.solr.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.common.util.Utils;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableMap;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+
+public class JsonSchemaValidator {
+ private final Map jsonSchema;
+
+
+ public JsonSchemaValidator(Map jsonSchema) {
+ this.jsonSchema = jsonSchema;
+ List<String> errs = new LinkedList<>();
+ validateObjectDef(jsonSchema, errs);
+ if(!errs.isEmpty()){
+ throw new RuntimeException("Invalid schema. "+ StrUtils.join(errs,'|'));
+ }
+ }
+
+ private void validateObjectDef(Map jsonSchema, List<String> errs) {
+ for (ObjectAttribute attr : ObjectAttribute.values()) {
+ attr.validate(jsonSchema, errs);
+ }
+ jsonSchema.keySet().forEach(o -> {
+ if (!knownAttributes.containsKey(o)) errs.add("Unknown key : " + o);
+ });
+ Map m = (Map) jsonSchema.get("properties");
+ if(m != null){
+ for (Object o : m.entrySet()) {
+ Map.Entry e = (Map.Entry) o;
+ if (e.getValue() instanceof Map) {
+ Map od = (Map) e.getValue();
+ validateObjectDef(od,errs);
+ } else {
+ errs.add("Invalid Object definition for field " +e.getKey());
+ }
+ }
+ }
+
+ }
+
+ public List<String> validateJson(Map json) {
+ return null;
+ }
+
+ enum ObjectAttribute {
+ type(true, Type.STRING),
+ properties(false, Type.OBJECT),
+ additionalProperties(false, Type.BOOLEAN),
+ required(false, Type.ARRAY),
+ items(false,Type.OBJECT),
+ __default(false,Type.UNKNOWN),
+ description(false, Type.ARRAY),
+ documentation(false, Type.STRING),
+ oneOf(false, Type.ARRAY),
+ id(false, Type.STRING),
+ _ref(false, Type.STRING),
+ _schema(false, Type.STRING);
+
+
+ final String key;
+ final boolean _required;
+ final Type typ;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void validate(Map attributeDefinition, List<String> errors) {
+ Object val = attributeDefinition.get(key);
+ if (val == null) {
+ if (_required) errors.add("Missing required attribute '" + key+ "' in object "+ Utils.toJSONString(attributeDefinition) );
+ } else {
+ if (!typ.validate(val)) errors.add(key + " should be of type " + typ._name);
+ }
+ }
+
+ ObjectAttribute(boolean required, Type type) {
+ this.key = name().replaceAll("__","").replace('_', '$');
+ this._required = required;
+ this.typ = type;
+ }
+ }
+
+ enum Type {
+ STRING {
+ @Override
+ boolean validate(Object o) {
+ return o instanceof String;
+ }
+ },
+ ARRAY {
+ @Override
+ boolean validate(Object o) {
+ return o instanceof List || o instanceof String;
+ }
+ },
+ NUMBER {
+ @Override
+ boolean validate(Object o) {
+ return o instanceof Number;
+ }
+ }, BOOLEAN {
+ @Override
+ boolean validate(Object o) {
+ return o instanceof Boolean;
+ }
+ }, OBJECT {
+ @Override
+ boolean validate(Object o) {
+ return o instanceof Map;
+ }
+ }, UNKNOWN {
+ @Override
+ boolean validate(Object o) {
+ return true;
+ }
+ };
+ final String _name;
+
+ Type() {
+ _name = this.name().toLowerCase(Locale.ROOT);
+ }
+
+ abstract boolean validate(Object o);
+
+ }
+
+ static final Map<String, ObjectAttribute> knownAttributes = unmodifiableMap(asList(ObjectAttribute.values()).stream().collect(toMap(ObjectAttribute::getKey, identity())));
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/cluster.security.RuleBasedAuthorization.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/cluster.security.RuleBasedAuthorization.json b/solr/core/src/resources/apispec/cluster.security.RuleBasedAuthorization.json
index 7dc50d6..694ba3a 100644
--- a/solr/core/src/resources/apispec/cluster.security.RuleBasedAuthorization.json
+++ b/solr/core/src/resources/apispec/cluster.security.RuleBasedAuthorization.json
@@ -10,6 +10,7 @@
},
"commands": {
"set-permission": {
+ "type":"object",
"description": "create a new permission, overwrite an existing permission definition, or assign a pre-defined permission to a role.",
"properties": {
"name":{
@@ -22,12 +23,18 @@
},
"collection":{
- "type":"list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description":"The collection or collections the permission will apply to. When the path that will be allowed is collection-specific, such as when setting permissions to allow use of the Schema API, omitting the collection property will allow the defined path and/or method for all collections. However, when the path is one that is non-collection-specific, such as the Collections API, the collection value must be null."
},
"path":{
- "type":"list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description":"A request handler name, such as /update or /select. A wild card is supported, to allow for all paths as appropriate (such as, /update/*)."
},
"index": {
@@ -50,6 +57,7 @@
]
},
"update-permission": {
+ "type":"object",
"properties": {
"name": {
"type": "string",
@@ -60,11 +68,17 @@
"description": "HTTP methods that are allowed for this permission. You could allow only GET requests, or have a role that allows PUT and POST requests. The method values that are allowed for this property are GET, POST, PUT, DELETE and HEAD."
},
"collection": {
- "type": "list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description": "The collection or collections the permission will apply to. When the path that will be allowed is collection-specific, such as when setting permissions to allow use of the Schema API, omitting the collection property will allow the defined path and/or method for all collections. However, when the path is one that is non-collection-specific, such as the Collections API, the collection value must be null."
},
"path": {
- "type": "list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description": "A request handler name, such as /update or /select. A wild card is supported, to allow for all paths as appropriate (such as, /update/*)."
},
"index": {
@@ -77,7 +91,6 @@
},
"params": {
"type": "object",
- "properties": {},
"additionalProperties": true,
"description": "The names and values of request parameters. This property can be omitted if all request parameters are allowed, but will restrict access only to the values provided if defined."
}
@@ -88,12 +101,13 @@
]
},
"delete-permission":{
+ "type":"object",
"description":"delete a permission by it's index",
"type":"int"
},
"set-user-role": {
+ "type":"object",
"description": "A single command allows roles to be mapped to users. To remove a user's permission, you should set the role to null. The key is always a user id and the value is one or more role names",
- "properties":{},
"additionalProperties":true
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/collections.Commands.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/collections.Commands.json b/solr/core/src/resources/apispec/collections.Commands.json
index 15e12c1..425ee96 100644
--- a/solr/core/src/resources/apispec/collections.Commands.json
+++ b/solr/core/src/resources/apispec/collections.Commands.json
@@ -28,7 +28,6 @@
"router": {
"type": "object",
"properties": {
- "type":"object",
"name": {
"type":"string",
"description" :"Router implementation compositeId or implicit",
@@ -61,32 +60,45 @@
"description": "When set to true, enables auto addition of replicas on shared file systems. See the section autoAddReplicas Settings for more details on settings and overrides."
},
"rule": {
- "type": "list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description":"replica placement rules. See the section Rule-based Replica Placement for details."
},
"snitch": {
- "type": "list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description":""
}
},
"required":["name"]
},
"create-alias":{
+ "type":"object",
"properties": {
"name": {
"type": "string",
"description": "The alias name to be created"
},
"collections" :{
- "type":"list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description":"The list of collections to be aliased"
}
},
"required" : ["name","collections"]
},
"delete-alias":{
- "type":"list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description":""
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/collections.collection.Commands.modify.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/collections.collection.Commands.modify.json b/solr/core/src/resources/apispec/collections.collection.Commands.modify.json
index 20e4cf7..5cfe6ae 100644
--- a/solr/core/src/resources/apispec/collections.collection.Commands.modify.json
+++ b/solr/core/src/resources/apispec/collections.collection.Commands.modify.json
@@ -3,11 +3,17 @@
"additionalProperties":true,
"properties":{
"rule": {
- "type": "list",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
"description": ""
},
"snitch": {
- "type": "list",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
"description": ""
},
"autoAddReplicas": {
@@ -18,6 +24,5 @@
"type": "string",
"description": ""
}
-
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/collections.collection.Commands.reload.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/collections.collection.Commands.reload.json b/solr/core/src/resources/apispec/collections.collection.Commands.reload.json
index 7d53e5b..17dd91d 100644
--- a/solr/core/src/resources/apispec/collections.collection.Commands.reload.json
+++ b/solr/core/src/resources/apispec/collections.collection.Commands.reload.json
@@ -1,3 +1,4 @@
{
+ "type" : "object",
"additionalProperties" : true
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/collections.collection.shards.Commands.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/collections.collection.shards.Commands.json b/solr/core/src/resources/apispec/collections.collection.shards.Commands.json
index 0c7e054..936a082 100644
--- a/solr/core/src/resources/apispec/collections.collection.shards.Commands.json
+++ b/solr/core/src/resources/apispec/collections.collection.shards.Commands.json
@@ -11,7 +11,7 @@
},
"commands": {
"create": {
- "properties":{},
+ "type":"object",
"additionalProperties":true
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/collections.collection.shards.shard.Commands.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/collections.collection.shards.shard.Commands.json b/solr/core/src/resources/apispec/collections.collection.shards.shard.Commands.json
index 356843e..1030ffa 100644
--- a/solr/core/src/resources/apispec/collections.collection.shards.shard.Commands.json
+++ b/solr/core/src/resources/apispec/collections.collection.shards.shard.Commands.json
@@ -31,9 +31,12 @@
"additionalProperties": true
},
"add-replica": {
+ "type" : "object",
"additionalProperties": true
},
"force-leader": {
+ "type" : "object",
+
"additionalProperties": true
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/core.SchemaEdit.addCopyField.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/core.SchemaEdit.addCopyField.json b/solr/core/src/resources/apispec/core.SchemaEdit.addCopyField.json
index a8c5eb7..8becd61 100644
--- a/solr/core/src/resources/apispec/core.SchemaEdit.addCopyField.json
+++ b/solr/core/src/resources/apispec/core.SchemaEdit.addCopyField.json
@@ -7,7 +7,10 @@
"description": "The source field"
},
"dest": {
- "type": "list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description": "A field or an array of fields to which the source field will be copied."
},
"maxChars": {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/core.SchemaEdit.addFieldType.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/core.SchemaEdit.addFieldType.json b/solr/core/src/resources/apispec/core.SchemaEdit.addFieldType.json
index bc28113..be6e2e9 100644
--- a/solr/core/src/resources/apispec/core.SchemaEdit.addFieldType.json
+++ b/solr/core/src/resources/apispec/core.SchemaEdit.addFieldType.json
@@ -1,4 +1,4 @@
{
- "properties":{},
+ "type":"object",
"additionalProperties": true
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/core.SchemaEdit.deleteCopyField.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/core.SchemaEdit.deleteCopyField.json b/solr/core/src/resources/apispec/core.SchemaEdit.deleteCopyField.json
index c209bc5..dd7b818 100644
--- a/solr/core/src/resources/apispec/core.SchemaEdit.deleteCopyField.json
+++ b/solr/core/src/resources/apispec/core.SchemaEdit.deleteCopyField.json
@@ -1,8 +1,9 @@
{
+ "type":"object",
"documentation":"https://cwiki.apache.org/confluence/display/solr/Schema+API#SchemaAPI-DeleteaField",
"properties":{
"name":{
- "descripion":"The delete-field command removes a field definition from your schema. If the field does not exist in the schema, or if the field is the source or destination of a copy field rule, an error is thrown",
+ "description":"The delete-field command removes a field definition from your schema. If the field does not exist in the schema, or if the field is the source or destination of a copy field rule, an error is thrown",
"type":"string"
}
},
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/core.SchemaEdit.deleteDynamicField.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/core.SchemaEdit.deleteDynamicField.json b/solr/core/src/resources/apispec/core.SchemaEdit.deleteDynamicField.json
index 2fe97f4..de27e24 100644
--- a/solr/core/src/resources/apispec/core.SchemaEdit.deleteDynamicField.json
+++ b/solr/core/src/resources/apispec/core.SchemaEdit.deleteDynamicField.json
@@ -1,3 +1,4 @@
{
+ "type":"object",
"additionalProperties":true
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/core.SchemaEdit.deleteFieldType.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/core.SchemaEdit.deleteFieldType.json b/solr/core/src/resources/apispec/core.SchemaEdit.deleteFieldType.json
index 2fe97f4..de27e24 100644
--- a/solr/core/src/resources/apispec/core.SchemaEdit.deleteFieldType.json
+++ b/solr/core/src/resources/apispec/core.SchemaEdit.deleteFieldType.json
@@ -1,3 +1,4 @@
{
+ "type":"object",
"additionalProperties":true
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/core.config.Commands.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/core.config.Commands.json b/solr/core/src/resources/apispec/core.config.Commands.json
index 0eb3274..e4d8cdc 100644
--- a/solr/core/src/resources/apispec/core.config.Commands.json
+++ b/solr/core/src/resources/apispec/core.config.Commands.json
@@ -15,7 +15,10 @@
"additionalProperties": true
},
"unset-property": {
- "type": "list"
+ "type":"array",
+ "items": {
+ "type": "string"
+ }
},
"add-requesthandler": {
"type": "object",
@@ -66,6 +69,7 @@
"additionalProperties": true
},
"add-queryparser": {
+ "type": "object",
"additionalProperties": true
},
"update-queryparser": {
@@ -139,7 +143,10 @@
"add-runtimelib": "core.config.Commands.addRuntimeLib",
"update-runtimelib": "core.config.Commands.updateRuntimeLib",
"delete-runtimelib": {
- "type": "list"
+ "type":"array",
+ "items": {
+ "type": "string"
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/core.config.Params.Commands.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/core.config.Params.Commands.json b/solr/core/src/resources/apispec/core.config.Params.Commands.json
index 8cc9210..a3b054c 100644
--- a/solr/core/src/resources/apispec/core.config.Params.Commands.json
+++ b/solr/core/src/resources/apispec/core.config.Params.Commands.json
@@ -16,7 +16,10 @@
"additionalProperties": true
},
"unset": {
- "type": "list",
+ "type":"array",
+ "items": {
+ "type": "string"
+ },
"description": "delete one or more parameter sets"
},
"update": {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/cores.Commands.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/cores.Commands.json b/solr/core/src/resources/apispec/cores.Commands.json
index dcba6145..ce93ccf 100644
--- a/solr/core/src/resources/apispec/cores.Commands.json
+++ b/solr/core/src/resources/apispec/cores.Commands.json
@@ -11,6 +11,7 @@
},
"commands": {
"create": {
+ "type" : "object",
"properties": {
"name": {
"type": "string",
@@ -57,6 +58,8 @@
"description": "The replica name"
},
"numShards": {
+ "type":"number",
+ "description":"NO:of shards"
}
},
"required": [
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/cores.core.Commands.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/cores.core.Commands.json b/solr/core/src/resources/apispec/cores.core.Commands.json
index 1c41605..dc089d1 100644
--- a/solr/core/src/resources/apispec/cores.core.Commands.json
+++ b/solr/core/src/resources/apispec/cores.core.Commands.json
@@ -11,12 +11,15 @@
},
"commands": {
"reload": {
- "properties": {}
+ "type":"object",
+ "additionalProperties":true
},
"unload": {
- "properties": {}
+ "type":"object",
+ "additionalProperties":true
},
"swap": {
+ "type":"object",
"properties": {
"with": {
"type": "string",
@@ -28,9 +31,14 @@
]
},
"merge-indexes": {
- "properties": {}
+ "type":"object",
+ "additionalProperties":true
+ },
+ "request-recovery": {
+ "type":"object",
+ "additionalProperties":true
+
},
- "request-recovery": {},
"split": "cores.core.Commands.split"
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/resources/apispec/cores.core.Commands.split.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/cores.core.Commands.split.json b/solr/core/src/resources/apispec/cores.core.Commands.split.json
index 6107586..fe38e42 100644
--- a/solr/core/src/resources/apispec/cores.core.Commands.split.json
+++ b/solr/core/src/resources/apispec/cores.core.Commands.split.json
@@ -3,14 +3,21 @@
"type": "object",
"properties": {
"path": {
- "type": "list",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
"description": "the directory path in which a piece of the index will be written"
},
"targetCore": {
- "type": "list",
- "descripton": "the target Solr core to which a piece of the index will be merged"
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "the target Solr core to which a piece of the index will be merged"
},
"splitKey": {
+ "type":"string",
"description": "The key to be used for splitting the index"
},
"ranges": {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c7f58b82/solr/core/src/test/org/apache/solr/util/JsonValidatorTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/util/JsonValidatorTest.java b/solr/core/src/test/org/apache/solr/util/JsonValidatorTest.java
new file mode 100644
index 0000000..cef319f
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/util/JsonValidatorTest.java
@@ -0,0 +1,55 @@
+package org.apache.solr.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Map;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.api.ApiBag;
+import org.apache.solr.common.util.Map2;
+
+public class JsonValidatorTest extends SolrTestCaseJ4 {
+
+ public void testSchema(){
+ checkSchema("collections.commands");
+ checkSchema("collections.collection.commands");
+ checkSchema("collections.collection.shards.Commands");
+ checkSchema("collections.collection.shards.shard.Commands");
+ checkSchema("collections.collection.shards.shard.replica.Commands");
+ checkSchema("cores.Commands");
+ checkSchema("cores.core.Commands");
+ checkSchema("cluster.security.BasicAuth.Commands");
+ checkSchema("cluster.security.RuleBasedAuthorization");
+ checkSchema("core.config.Commands");
+ checkSchema("core.SchemaEdit");
+ }
+
+ private void checkSchema(String name) {
+ Map2 spec = ApiBag.getSpec(name).getSpec();
+ Map commands = (Map) spec.get("commands");
+ for (Object o : commands.entrySet()) {
+ Map.Entry cmd = (Map.Entry) o;
+ try {
+ new JsonSchemaValidator((Map) cmd.getValue());
+ } catch (Exception e) {
+ throw new RuntimeException("Error in command "+ cmd.getKey() +" in schema "+name, e);
+ }
+ }
+ }
+
+}