mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2025-10-16 12:05:38 +00:00
REST API: Support the patternProperties JSON Schema keyword.
Props yakimun. Fixes #51024. git-svn-id: https://develop.svn.wordpress.org/trunk@49082 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
062db8120d
commit
f60aa51988
@ -1539,6 +1539,42 @@ function rest_stabilize_value( $value ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the JSON Schema pattern matches a value.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param string $pattern The pattern to match against.
|
||||
* @param string $value The value to check.
|
||||
* @return bool True if the pattern matches the given value, false otherwise.
|
||||
*/
|
||||
function rest_validate_json_schema_pattern( $pattern, $value ) {
|
||||
$escaped_pattern = str_replace( '#', '\\#', $pattern );
|
||||
|
||||
return 1 === preg_match( '#' . $escaped_pattern . '#u', $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the schema for a property using the patternProperties keyword.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param string $property The property name to check.
|
||||
* @param array $args The schema array to use.
|
||||
* @return array|null The schema of matching pattern property, or null if no patterns match.
|
||||
*/
|
||||
function rest_find_matching_pattern_property_schema( $property, $args ) {
|
||||
if ( isset( $args['patternProperties'] ) ) {
|
||||
foreach ( $args['patternProperties'] as $pattern => $child_schema ) {
|
||||
if ( rest_validate_json_schema_pattern( $pattern, $property ) ) {
|
||||
return $child_schema;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a value based on a schema.
|
||||
*
|
||||
@ -1553,6 +1589,7 @@ function rest_stabilize_value( $value ) {
|
||||
* Validate required properties.
|
||||
* @since 5.6.0 Support the "minProperties" and "maxProperties" keywords for objects.
|
||||
* Support the "multipleOf" keyword for numbers and integers.
|
||||
* Support the "patternProperties" keyword for objects.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $args Schema array to use for validation.
|
||||
@ -1650,7 +1687,19 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) {
|
||||
if ( is_wp_error( $is_valid ) ) {
|
||||
return $is_valid;
|
||||
}
|
||||
} elseif ( isset( $args['additionalProperties'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args );
|
||||
if ( null !== $pattern_property_schema ) {
|
||||
$is_valid = rest_validate_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' );
|
||||
if ( is_wp_error( $is_valid ) ) {
|
||||
return $is_valid;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $args['additionalProperties'] ) ) {
|
||||
if ( false === $args['additionalProperties'] ) {
|
||||
/* translators: %s: Property of an object. */
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not a valid property of Object.' ), $property ) );
|
||||
@ -1744,12 +1793,9 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) {
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $args['pattern'] ) ) {
|
||||
$pattern = str_replace( '#', '\\#', $args['pattern'] );
|
||||
if ( ! preg_match( '#' . $pattern . '#u', $value ) ) {
|
||||
/* translators: 1: Parameter, 2: Pattern. */
|
||||
return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) );
|
||||
}
|
||||
if ( isset( $args['pattern'] ) && ! rest_validate_json_schema_pattern( $args['pattern'], $value ) ) {
|
||||
/* translators: 1: Parameter, 2: Pattern. */
|
||||
return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -1897,7 +1943,16 @@ function rest_sanitize_value_from_schema( $value, $args, $param = '' ) {
|
||||
foreach ( $value as $property => $v ) {
|
||||
if ( isset( $args['properties'][ $property ] ) ) {
|
||||
$value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' );
|
||||
} elseif ( isset( $args['additionalProperties'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args );
|
||||
if ( null !== $pattern_property_schema ) {
|
||||
$value[ $property ] = rest_sanitize_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $args['additionalProperties'] ) ) {
|
||||
if ( false === $args['additionalProperties'] ) {
|
||||
unset( $value[ $property ] );
|
||||
} elseif ( is_array( $args['additionalProperties'] ) ) {
|
||||
@ -2053,6 +2108,7 @@ function rest_parse_embed_param( $embed ) {
|
||||
* Filters the response to remove any fields not available in the given context.
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @since 5.6.0 Support the "patternProperties" keyword for objects.
|
||||
*
|
||||
* @param array|object $data The response data to modify.
|
||||
* @param array $schema The schema for the endpoint used to filter the response.
|
||||
@ -2093,8 +2149,13 @@ function rest_filter_response_by_context( $data, $schema, $context ) {
|
||||
} elseif ( $is_object_type ) {
|
||||
if ( isset( $schema['properties'][ $key ] ) ) {
|
||||
$check = $schema['properties'][ $key ];
|
||||
} elseif ( $has_additional_properties ) {
|
||||
$check = $schema['additionalProperties'];
|
||||
} else {
|
||||
$pattern_property_schema = rest_find_matching_pattern_property_schema( $key, $schema );
|
||||
if ( null !== $pattern_property_schema ) {
|
||||
$check = $pattern_property_schema;
|
||||
} elseif ( $has_additional_properties ) {
|
||||
$check = $schema['additionalProperties'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2132,6 +2193,7 @@ function rest_filter_response_by_context( $data, $schema, $context ) {
|
||||
* Sets the "additionalProperties" to false by default for all object definitions in the schema.
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @since 5.6.0 Support the "patternProperties" keyword.
|
||||
*
|
||||
* @param array $schema The schema to modify.
|
||||
* @return array The modified schema.
|
||||
@ -2146,6 +2208,12 @@ function rest_default_additional_properties_to_false( $schema ) {
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $schema['patternProperties'] ) ) {
|
||||
foreach ( $schema['patternProperties'] as $key => $child_schema ) {
|
||||
$schema['patternProperties'][ $key ] = rest_default_additional_properties_to_false( $child_schema );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $schema['additionalProperties'] ) ) {
|
||||
$schema['additionalProperties'] = false;
|
||||
}
|
||||
@ -2300,6 +2368,7 @@ function rest_get_endpoint_args_for_schema( $schema, $method = WP_REST_Server::C
|
||||
'items',
|
||||
'properties',
|
||||
'additionalProperties',
|
||||
'patternProperties',
|
||||
'minProperties',
|
||||
'maxProperties',
|
||||
'minimum',
|
||||
|
||||
@ -1284,6 +1284,42 @@ class Tests_REST_API extends WP_UnitTestCase {
|
||||
),
|
||||
array( 'additional' => array( 'a' => '1' ) ),
|
||||
),
|
||||
'pattern properties' => array(
|
||||
array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'a' => array(
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
),
|
||||
'patternProperties' => array(
|
||||
'[0-9]' => array(
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'c.*' => array(
|
||||
'type' => 'string',
|
||||
'context' => array( 'edit' ),
|
||||
),
|
||||
),
|
||||
'additionalProperties' => array(
|
||||
'type' => 'string',
|
||||
'context' => array( 'edit' ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'a' => '1',
|
||||
'b' => '2',
|
||||
'0' => '3',
|
||||
'ca' => '4',
|
||||
),
|
||||
array(
|
||||
'a' => '1',
|
||||
'0' => '3',
|
||||
),
|
||||
),
|
||||
'multiple types object' => array(
|
||||
array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
|
||||
@ -291,7 +291,14 @@ class WP_Test_REST_Controller extends WP_Test_REST_TestCase {
|
||||
$this->assertArrayHasKey( $property, $args['somearray'] );
|
||||
}
|
||||
|
||||
foreach ( array( 'properties', 'additionalProperties', 'minProperties', 'maxProperties' ) as $property ) {
|
||||
$object_properties = array(
|
||||
'properties',
|
||||
'patternProperties',
|
||||
'additionalProperties',
|
||||
'minProperties',
|
||||
'maxProperties',
|
||||
);
|
||||
foreach ( $object_properties as $property ) {
|
||||
$this->assertArrayHasKey( $property, $args['someobject'] );
|
||||
}
|
||||
|
||||
|
||||
@ -237,6 +237,102 @@ class WP_Test_REST_Schema_Sanitization extends WP_UnitTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 51024
|
||||
*
|
||||
* @dataProvider data_type_object_pattern_properties
|
||||
*
|
||||
* @param array $pattern_properties
|
||||
* @param array $value
|
||||
* @param array $expected
|
||||
*/
|
||||
public function test_type_object_pattern_properties( $pattern_properties, $value, $expected ) {
|
||||
$schema = array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'propA' => array( 'type' => 'string' ),
|
||||
),
|
||||
'patternProperties' => $pattern_properties,
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
|
||||
$this->assertSame( $expected, rest_sanitize_value_from_schema( $value, $schema ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function data_type_object_pattern_properties() {
|
||||
return array(
|
||||
array( array(), array(), array() ),
|
||||
array( array(), array( 'propA' => 'a' ), array( 'propA' => 'a' ) ),
|
||||
array(
|
||||
array(),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'propB' => 'b',
|
||||
),
|
||||
array( 'propA' => 'a' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'propB' => array( 'type' => 'string' ),
|
||||
),
|
||||
array( 'propA' => 'a' ),
|
||||
array( 'propA' => 'a' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'propB' => array( 'type' => 'string' ),
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'propB' => 'b',
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'propB' => 'b',
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'.*C' => array( 'type' => 'string' ),
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'propC' => 'c',
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'propC' => 'c',
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'[0-9]' => array( 'type' => 'integer' ),
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'prop0' => '0',
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'prop0' => 0,
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'.+' => array( 'type' => 'string' ),
|
||||
),
|
||||
array(
|
||||
'' => '',
|
||||
'propA' => 'a',
|
||||
),
|
||||
array( 'propA' => 'a' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_type_object_nested() {
|
||||
$schema = array(
|
||||
'type' => 'object',
|
||||
|
||||
@ -288,6 +288,107 @@ class WP_Test_REST_Schema_Validation extends WP_UnitTestCase {
|
||||
$this->assertWPError( rest_validate_value_from_schema( array( 'a' => 'invalid' ), $schema ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 51024
|
||||
*
|
||||
* @dataProvider data_type_object_pattern_properties
|
||||
*
|
||||
* @param array $pattern_properties
|
||||
* @param array $value
|
||||
* @param bool $expected
|
||||
*/
|
||||
public function test_type_object_pattern_properties( $pattern_properties, $value, $expected ) {
|
||||
$schema = array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'propA' => array( 'type' => 'string' ),
|
||||
),
|
||||
'patternProperties' => $pattern_properties,
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
|
||||
if ( $expected ) {
|
||||
$this->assertTrue( rest_validate_value_from_schema( $value, $schema ) );
|
||||
} else {
|
||||
$this->assertWPError( rest_validate_value_from_schema( $value, $schema ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function data_type_object_pattern_properties() {
|
||||
return array(
|
||||
array( array(), array(), true ),
|
||||
array( array(), array( 'propA' => 'a' ), true ),
|
||||
array(
|
||||
array(),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'propB' => 'b',
|
||||
),
|
||||
false,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'propB' => array( 'type' => 'string' ),
|
||||
),
|
||||
array( 'propA' => 'a' ),
|
||||
true,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'propB' => array( 'type' => 'string' ),
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'propB' => 'b',
|
||||
),
|
||||
true,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'.*C' => array( 'type' => 'string' ),
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'propC' => 'c',
|
||||
),
|
||||
true,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'[0-9]' => array( 'type' => 'integer' ),
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'prop0' => 0,
|
||||
),
|
||||
true,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'[0-9]' => array( 'type' => 'integer' ),
|
||||
),
|
||||
array(
|
||||
'propA' => 'a',
|
||||
'prop0' => 'notAnInteger',
|
||||
),
|
||||
false,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'.+' => array( 'type' => 'string' ),
|
||||
),
|
||||
array(
|
||||
'' => '',
|
||||
'propA' => 'a',
|
||||
),
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_type_object_additional_properties_false() {
|
||||
$schema = array(
|
||||
'type' => 'object',
|
||||
|
||||
@ -121,6 +121,11 @@ class WP_REST_Test_Controller extends WP_REST_Controller {
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
'patternProperties' => array(
|
||||
'[0-9]' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'minProperties' => 1,
|
||||
'maxProperties' => 10,
|
||||
'ignored_prop' => 'ignored_prop',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user