Opened 2 years ago
Last modified 22 months ago
#56483 new defect (bug)
Weird oneOf behaviour upon validation of post meta value upon GET retrieval via REST API
Reported by: | joeyojoeyo12 | Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | major | Version: | 6.0.1 |
Component: | REST API | Keywords: | reporter-feedback |
Focuses: | rest-api | Cc: |
Description
I've been checking for a significant amount of time with some other developers, and we can't find reasonable explanations for the behavior described here: https://wordpress.stackexchange.com/questions/409131/oneof-json-schema-validation-not-properly-working-for-custom-post-meta-value
Change History (6)
#2
@
2 years ago
Hi, I was able to reproduce the response having null
with your workflow, but do not know if the cause is the same as in your case. I have misspelled a value which was defined with pattern
in the input meta. Would you mind sharing your input meta here?
So in my case I had inserted an invalid meta with add_post_meta()
, and wanted the fetch it with REST API
. But REST API
validates the value against the schema, and because it was invalid, so the response was null
.
One might argue that a detailed error response would be more useful, however a better argue would be to use REST API
for all operations (create, read, update, delete), so all database operations will be validated against the schema.
So my next try was using REST API
instead of add_post_meta()
, and the response immediately was an error message with detailed information about the misspelled value. It was not possible to create an invalid meta at the first place!
As I said before, it is possible that your issue is caused by something else! It would be nice :
- to see your input meta here, so we can rule out the invalid meta issue, and
- if you could try to use
REST API
for creating the meta, and see what would be the response to that.
#4
@
2 years ago
I'm sorry you're experiencing issues @joeyojoeyo12. Could you try and simplify your replication example to the bare minimum required to trigger the error? It is a bit difficult to follow such a large example.
To clarify a couple of things. Yes, when using oneOf
, the REST API ensures that exactly one schema matches the input value. If more than one schema matches successfully, then the validation will fail. If this isn't the behavior you are looking for, try using the anyOf
keyword. This indicates that you want to match any of the provided schemas.
Can you clarify where you saw oneOf
overwritten to anyOf
? As far as I known, we never assign a value of anyOf
to a schema.
Omitting type
when setting your schema in show_in_rest
won't actually remove the type
definition because WP_REST_Meta_Fields::get_registered_fields
defaults the type
keyword to the top-level type
you set in register_meta
.
While not required, specifying a type
in addition to a oneOf
or anyOf
keyword is supported. However, that type
must then be valid for all schemas used in oneOf
or anyOf
.
Validation is supposed to continue after the oneOf
or anyOf
keywords are processed successfully. So if you are registering a meta key where we default additionalProperties
to be false
, then it would be expected behavior that your schema validation fails. I believe you should be able to work around this by also setting additionalProperties
to true
at the same level that you set oneOf
.
#5
@
2 years ago
Hey there; apologies on my late reply and thanks for your help; I've added a simpler example above that highlights the issues I'm having.
#6
@
22 months ago
In the future @joeyojoeyo12, it'd be best to add the details in the follow up comment instead of editing the first one. It makes things easier to track that way.
A) The consequence of this is that I cannot block incoming requests that have more data in the payload than needed, correct?
You can using additionalProperties
.
B) So I figured you have to specify it at both levels; can you confirm that?
You can omit it, but you have to then specify your schema callbacks manually, see my next reply.
C) do I still have to provide it explicitly to every argument?
It depends on how you register your route. If you use WP_REST_Controller::get_item_schema
combined with WP_REST_Controller::get_endpoint_args_for_item_schema
or simply rest_get_endpoint_args_for_schema
, the args
definition for your route will have the default JSON Schema validation and sanitization callbacks automatically applied.
Otherwise, WP_REST_Request
will automatically apply the schema sanitization via rest_parse_request_arg
but only if the arg has a specified type
.
D) can you in WP JSON Schema maybe directly tell the args that the payload is a simply object; basically under no key?
No. The REST API doesn't support this behavior. A lot of the APIs are formulated around request objects being a dictionary with parameter keys that each have a set of validation and sanitization bits attached to it. This may be supported officially in the future, but it would take some doing.
In the mean time, you could set a validate_callback
when you register your REST API route alongside callback
and permission_callback
. It is passed the entire WP_REST_Request
object. Then you can call the sanitize and validate callbacks directly.
Here is a corrected schema.
[ 'data' => [ 'sanitize_callback' => 'rest_sanitize_request_arg', 'validate_callback' => 'rest_validate_request_arg', 'required' => true, 'oneOf' => [ [ 'title' => 'only_banana', 'type' => 'object', 'additionalProperties' => false, 'properties' => $banana_attributes ], [ 'title' => 'banana_and_apple', 'type' => 'object', 'additionalProperties' => false, 'properties' => array_merge( $banana_attributes, $apple_attributes ) ], [ 'title' => 'banana_and_apple_and_pear', 'type' => 'object', 'additionalProperties' => false, 'properties' => array_merge( $banana_attributes, $apple_attributes, $pear_attributes ) ] ] ] ]
Another example, eventually illustrating some deeper rooted problems:
With this setting, try to add a new
, and then to try to retrieve it via
.
SIMPLER EXAMPLE
Here you go with another, much simpler example, this time with a REST route. Let's say you have an endpoint to which you want to submit fruit data. Every distinct fruit (banana, apple, pear) has its own set of attributes:
You want to implement JSON Schema logic using
oneOf
that validates an input payload to contain either only banana attributes, banana and apple attributes or banana and apple and pear attributes. A payload like this would for example pass:So I would do something like:
Now with this, I have two problems:
A) As @TimothyBlynJacobs said; the above code will block incoming requests with a
400
. You actually have to setadditionalProperties
on the level of theoneOf
key totrue
, or simply omit theadditionalProperties
key there (which I believe results in the same, as the default value of that one istrue
). The consequence of this is that I cannot block incoming requests that have more data in the payload than needed, correct?B) If I'm understanding @TimothyBlynJacobs correctly, he mentioned that setting
type => object
on the same level asoneOf
is not necessary. But you will notice in this example, that if you omit thetype
key at theoneOf
level, the validation will no longer work. So I figured you have to specify it at both levels; can you confirm that?Then, I also have a question regarding how WordPress executes validation and sanitization based on JSON Schema:
C) When I started with WP REST, I've always specified:
For every argument, to ensure that all arguments are both validated and sanitized according to what I specified in the JSON Schema. I now noticed that WP seems to do at least the validation by default; even if I omit the above-mentioned. Does WP now actually automatically trigger the default validation
rest_validate_request_arg
and sanitizationrest_sanitize_request_arg
functions based on what you've provided for your JSON schema? Or do I stell have to provide it explicitly to every argument?D) In my example above, to simplify the JSON Schema related to the usecase, I've grouped the submitted payload into an inner object key
data
. This means that I now have to adapt the way I submit payloads to endpoints usingoneOf
logic; by wrapping the payloads in an inner{"data":{}}
object. Is that the way to go; or can you in WP JSON Schema maybe directly tell theargs
that the payload is a simply object; basically under no key?