Make WordPress Core

Changes between Initial Version and Version 1 of Ticket #56483, comment 1


Ignore:
Timestamp:
11/14/2022 02:01:28 PM (3 years ago)
Author:
joeyojoeyo12
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #56483, comment 1

    initial v1  
    335335}}}
    336336.
     337
     338
     339**SIMPLER EXAMPLE**
     340
     341Here 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:
     342
     343{{{#!php
     344<?php
     345$banana_attributes = [
     346    "bananas_color" => [
     347        'type' => 'string',
     348        'pattern' => '^yellow$',
     349        'required' => true
     350    ],
     351    "bananas_age" => [
     352        'type' => 'integer',
     353        'enum' => [2,3,4,5],
     354        'required' => true
     355    ]
     356];
     357
     358$apple_attributes = [
     359    "apples_color" => [
     360        'type' => 'string',
     361        'pattern' => '^red$',
     362        'required' => true
     363    ],
     364    "apples_radius" => [
     365        'type' => 'integer',
     366        'enum' => [2,3,4,5],
     367        'required' => true
     368    ]
     369];
     370
     371$pear_attributes = [
     372    "pears_color" => [
     373        'type' => 'string',
     374        'pattern' => '^green$',
     375        'required' => true
     376    ],
     377    "pears_height" => [
     378        'type' => 'integer',
     379        'enum' => [2,3,4,5],
     380        'required' => true
     381    ]
     382];
     383}}}
     384
     385You 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:
     386
     387{{{
     388{
     389  "data": {
     390    "bananas_color": "yellow",
     391    "bananas_age": 4,
     392    "apples_color": "red",
     393    "apples_radius": 3,
     394    "pears_color": "green",
     395    "pears_height": 5
     396  }
     397}
     398}}}
     399
     400So I would do something like:
     401
     402{{{#!php
     403<?php
     404register_rest_route(
     405    'mynamespace/v1',
     406    '/fruits',
     407    [
     408        [
     409            'methods'             => 'POST',
     410            'permission_callback' => '__return_true',
     411            'callback'            => [
     412                FruitsController::class,
     413                'process'
     414            ],
     415            'args'                => [
     416                'data' => [
     417                    'type'     => 'object',
     418                    'additionalProperties' => false,
     419                    'required' => true,
     420                    'oneOf'    => [
     421                        [
     422                            'title'                => 'only_banana',
     423                            'type'                 => 'object',
     424                            'additionalProperties' => false,
     425                            'properties'           => $banana_attributes
     426                        ],
     427                        [
     428                            'title'                => 'banana_and_apple',
     429                            'type'                 => 'object',
     430                            'additionalProperties' => false,
     431                            'properties'           => array_merge(
     432                                $banana_attributes,
     433                                $apple_attributes
     434                            )
     435                        ],
     436                        [
     437                            'title'                => 'banana_and_apple_and_pear',
     438                            'type'                 => 'object',
     439                            'additionalProperties' => false,
     440                            'properties'           => array_merge(
     441                                $banana_attributes,
     442                                $apple_attributes,
     443                                $pear_attributes
     444                            )
     445                        ]
     446                    ]
     447                ]
     448            ]
     449        ]
     450    ]
     451);
     452}}}
     453
     454
     455Now with this, I have two problems:
     456
     457
     458**A)** As @TimothyBlynJacobs said; the above code will block incoming requests with a `400`. You actually have to set `additionalProperties` on the level of the `oneOf` key to `true`, or simply omit the `additionalProperties` key there (which I believe results in the same, as the default value of that one is `true`). The consequence of this is that I cannot block incoming requests that have more data in the payload than needed, correct?
     459
     460**B)** If I'm understanding @TimothyBlynJacobs correctly, he mentioned that setting `type => object` on the same level as `oneOf` is not necessary. But you will notice in this example, that if you omit the `type` key at the `oneOf` level, the validation will no longer work. So I figured you have to specify it at both levels; can you confirm that?
     461
     462Then, I also have a question regarding how WordPress executes validation and sanitization based on JSON Schema:
     463
     464**C)** When I started with WP REST, I've always specified:
     465
     466{{{
     467'validate_callback' => 'rest_validate_request_arg',
     468'sanitize_callback' => 'rest_sanitize_request_arg'
     469}}}
     470
     471For 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 sanitization `rest_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?
     472
     473**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 using `oneOf` 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 the `args` that the payload is a simply object; basically under no key?