swaggest / php-json-schema

High definition PHP structures with JSON-schema based validation
MIT License
438 stars 50 forks source link

[bug]Priority of 'allOf' with 'required' #157

Closed chatugpt closed 7 months ago

chatugpt commented 7 months ago

schema: { "$schema": "https://schemas.amazon.com/selling-partners/definitions/product-types/meta-schema/v1", "$id": "https://schemas.amazon.com/selling-partners/definitions/product-types/schema/v1/SHIRT", "$comment": "Amazon product type definition for SHIRT product type", "$defs": { "marketplace_id": { "default": "ATVPDKIKX0DER", "editable": false, "hidden": true, "examples": [ "Amazon.com" ], "type": "string", "anyOf": [ { "type": "string" }, { "type": "string", "enum": [ "ATVPDKIKX0DER" ], "enumNames": [ "Amazon.com" ] } ] }, "language_tag": { "default": "en_US", "editable": false, "hidden": true, "examples": [ "English (United States)" ], "type": "string", "anyOf": [ { "type": "string" }, { "type": "string", "enum": [ "en_US" ], "enumNames": [ "English (United States)" ] } ] } }, "type": "object", "required": [ "parentage_level" ], "properties": { "item_name": { "title": "Item Name", "description": "Provide a name for the item including brand, color, fit and size, if available.", "examples": [ "Tommy Bahama 'Shades of Paradise' Polo Large Blue" ], "type": "array", "minItems": 1, "minUniqueItems": 1, "maxUniqueItems": 1, "selectors": [ "marketplace_id", "language_tag" ], "items": { "type": "object", "required": [ "language_tag", "marketplace_id", "value" ], "properties": { "value": { "title": "Item Name", "description": "Provide a name for the item including brand, color, fit and size, if available.", "editable": true, "hidden": false, "examples": [ "Robert Graham Men's Maya Bay Short Sleeve Classic Fit Shirt, Cranberry, X-Large" ], "type": "string", "minLength": 0, "maxLength": 200 }, "language_tag": { "$ref": "#/$defs/language_tag" }, "marketplace_id": { "$ref": "#/$defs/marketplace_id" } }, "additionalProperties": false } }, "brand": { "title": "Brand Name", "description": "An alphanumeric string; 1 character minimum in length and 50 characters maximum in length.", "examples": [ "Ralph Lauren" ], "type": "array", "minItems": 1, "minUniqueItems": 1, "maxUniqueItems": 1, "selectors": [ "marketplace_id", "language_tag" ], "items": { "type": "object", "required": [ "language_tag", "marketplace_id", "value" ], "properties": { "value": { "title": "Brand Name", "description": "Provide the brand name of the product", "editable": false, "hidden": false, "examples": [ "Calvin Klein, Hanes, Goodthreads, Under Armour" ], "type": "string", "minLength": 1, "maxLength": 100 }, "language_tag": { "$ref": "#/$defs/language_tag" }, "marketplace_id": { "$ref": "#/$defs/marketplace_id" } }, "additionalProperties": false } }, "model_name": { "title": "Model Name", "description": "Specify the model name of the product as defined by the manufacturer or brand excluding item type, color, brand or size", "examples": [ "Camedia" ], "type": "array", "minItems": 1, "minUniqueItems": 1, "maxUniqueItems": 1, "selectors": [ "marketplace_id", "language_tag" ], "items": { "type": "object", "required": [ "language_tag", "marketplace_id", "value" ], "properties": { "value": { "title": "Model Name", "description": "Specify the model name of the product as defined by the manufacturer or brand excluding item type, color, brand or size", "editable": true, "hidden": false, "examples": [ "Maya Bay Short Sleeve Classic Fit Shirt" ], "type": "string", "minLength": 1, "maxLength": 120 }, "language_tag": { "$ref": "#/$defs/language_tag" }, "marketplace_id": { "$ref": "#/$defs/marketplace_id" } }, "additionalProperties": false } }, "parentage_level": { "title": "Parentage Level", "description": "Specify whether a SKU is a parent or child", "examples": [ "Parent" ], "type": "array", "minItems": 1, "minUniqueItems": 1, "maxUniqueItems": 1, "selectors": [ "marketplace_id" ], "items": { "type": "object", "required": [ "marketplace_id" ], "properties": { "value": { "title": "Parentage Level", "description": "Specify whether a SKU is a parent or child", "editable": true, "hidden": false, "examples": [ "Parent" ], "type": "string", "enum": [ "child", "parent" ], "enumNames": [ "Child", "Parent" ] }, "marketplace_id": { "$ref": "#/$defs/marketplace_id" }, "test_all": { "title": "Parentage Level", "editable": true, "type": "string" } }, "additionalProperties": false } } }, "allOf": [ { "allOf": [ { "if": { "required": [ "parentage_level" ] }, "then": { "required": [ "model_name" ] }, "else": { "required": [ "brand" ] } } ] }, { "allOf": [ { "if": { "required": [ "parentage_level" ] }, "then": { "required": [ "item_name" ] } } ] } ] }

php code : $schema = Schema::import(json_decode($schemaString)); $schema->in(json_decode('{}')); result: Required property missing: brand;

expect: Required property missing: parentage_level;

allOf takes precedence over required ?

vearutop commented 7 months ago

In general, there is no such thing as precedence for JSON schema keywords. Keyword validation works independently (except for some newer things like unevaluatedProperties).

The order in which keywords are checked is not a bug, but an implementation detail that is not governed by spec.

A limitation of this library is that it stops processing on the first failure (to fail fast), otherwise you'd see both Required property missing: brand and Required property missing: parentage_level in the list of errors.

The schema that you shared does not make much sense to me, since root "required" invalidates some of the logical conditions in "allOf" (e.g. {"brand":"foo"} would not be valid).

  "required": [
    "parentage_level"
  ],
  "allOf": [
    {
      "allOf": [
        {
          "if": {
            "required": [
              "parentage_level"
            ]
          },
          "then": {
            "required": [
              "model_name"
            ]
          },
          "else": {
            "required": [
              "brand"
            ]
          }
        }
      ]
    }