ArrayDef supports validation of array structures using JSON schema (draft 4). However, JSON schema can be cumbersome to write. We should provide convenience methods for covering the simple use case. For example, it should be possible to declare a list of strings as follows:
"foobar" => [ ParamValidator::PARAM_TYPE => 'array', Handler::PARAM_SOURCE => 'body', ArrayDef::PARAM_SCHEMA => ArrayDef::makeListSchema( 'string' ), ]
Calls to the convenience methods can be nested to create more complex schemas, e.g. to create a list of objects that each contain a string and a map of booleans:
ArrayDef::PARAM_SCHEMA => ArrayDef::makeListSchema( ArrayDef::makeObjectSchema( [ 'something' => 'string', 'otherthing' => ArrayDef::makeMapSchema( 'boolean' ) ] ); ),
The following three convenience methods should be supported:
makeListSchema( $itemSchema ): returns a JSON schema of type array with the given schema for each item. If $itemSchema is a string, it will be interpreted as a type (so the schema would be [ 'type' => $itemSchema ].
makeMapSchema( $entrySchema ): returns a JSON schema of type object with the given schema for each entry (using additionalProperties ). If $itemSchema is a string, it will be interpreted as a type (so the schema would be [ 'type' => $itemSchema ]. We may need to use some custom hackers to support numeric keys.
makeObjectSchema( $requiredProperties, $optionalProperties = [], $additionalProperties= false ): returns a JSON schema of type object with properties defined as follows (details to be decided):
- $optionalProperties is a map of parameter names to their respective schemas. If a schema is given as a string, it's considered to be shorthand for as type, like in makeListSchema and makeListSchema.
- $requiredProperties is a map like $optionalProperties, or it's a list of names of required properties. $optionalProperties must not be a list.
- properties is defined by merging $requiredProperties and $optionalProperties (if $requiredProperties is a list, use only $optionalProperties).
- required is defined by taking the array keys of $requiredProperties (or just $requiredProperties, if it's a map).
- additionalProperties is taken from $additionalProperties, which defaults to false. If it's a string, it's considered to be the type of the schema, as above.
Some examples:
ArrayDef::makeObjectSchema( [ 'a' => 'integer', 'b' => [ 'enum' => [ 'x', 'y', 'z' ] ] ] ) becomes:
{ "type": "object", "required": [ "a", "b" ], "properties": [ "a" => { "type": "integer" }, "b" => { "enum": "x", "y", "z" }, ] }
ArrayDef::makeObjectSchema( [ 'a' => 'integer' ], [ 'b' => [ 'enum' => [ 'x', 'y', 'z' ] ] ] ) becomes:
{ "type": "object", "required": [ "a" ], "properties": [ "a" => { "type": "integer" }, "b" => { "enum": "x", "y", "z" }, ] }
ArrayDef::makeObjectSchema( [ 'a' ], [ 'a' => 'integer', 'b' => [ 'enum' => [ 'x', 'y', 'z' ] ] ] ) would be the same.
ArrayDef::makeObjectSchema( [ 'a' ], [], 'string' ) becomes:
{ "type": "object", "required": [ "a" ], "additionalProperties": [ "type": "string ] }