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);
+      }
+    }
+  }
+
+}