From 69c07ef134fb6f79b38c0b970919af135a7b02ca Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 18 Oct 2021 14:25:26 +0200 Subject: [PATCH 1/8] Added basic information about current OpenAPI version --- src/spec/OpenApi.php | 34 +++++++++++++++++++++++++++++++++- tests/spec/OpenApiTest.php | 28 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 29d38b3..1cb4206 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -27,6 +27,15 @@ */ class OpenApi extends SpecBaseObject { + const VERSION_3_0 = '3.0.x'; + const VERSION_3_1 = '3.1.x'; + const VERSION_UNSUPPORTED = 'unsupported'; + + /** + * Pattern used to validate OpenAPI versions. + */ + const PATTERN_VERSION = '/^(3\.(0|1))\.\d+(-rc\d)?$/i'; + /** * @return array array of attributes available in this object. */ @@ -75,8 +84,31 @@ public function __get($name) public function performValidation() { $this->requireProperties(['openapi', 'info', 'paths']); - if (!empty($this->openapi) && !preg_match('/^3\.0\.\d+(-rc\d)?$/i', $this->openapi)) { + if (!empty($this->openapi) && !preg_match(static::PATTERN_VERSION, $this->openapi)) { $this->addError('Unsupported openapi version: ' . $this->openapi); } } + + /** + * Returns the OpenAPI major version of the loaded OpenAPI description. + * @return string This returns a value of one of the `VERSION_*`-constants. Currently supported versions are: + * + * - `VERSION_3_0 = '3.0.x'` + * - `VERSION_3_1 = '3.1.x'` + * + * For unsupported version, this function will return `VERSION_UNSUPPORTED = 'unsupported'` + */ + public function getMajorVersion() + { + if (preg_match(static::PATTERN_VERSION, $this->openapi, $matches)) { + switch ($matches[1]) { + case '3.0': + return static::VERSION_3_0; + case '3.1': + return static::VERSION_3_1; + } + } + + return self::VERSION_UNSUPPORTED; + } } diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index bf321f1..7732040 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -1,6 +1,7 @@ assertTrue($openapi->validate(), print_r($openapi->getErrors(), true)); + $this->assertEquals('3.0.x', $openapi->getMajorVersion()); + + $yaml = <<assertTrue($openapi->validate(), print_r($openapi->getErrors(), true)); + $this->assertEquals('3.1.x', $openapi->getMajorVersion()); + + + } } From 82b0db7fe2780c4311eda6a71104f526ec3ae7db Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 18 Oct 2021 14:46:57 +0200 Subject: [PATCH 2/8] OpenAPI v3.1: Add schema and validation to CLI Co-authored-by: Cees-Jan Kiewiet --- Makefile | 9 +- bin/php-openapi | 12 +- composer.json | 18 +- schemas/openapi-v3.1.json | 1340 ++++++++++++++++++++++++++++++++++++ schemas/openapi-v3.1.yaml | 911 ++++++++++++++++++++++++ src/spec/OpenApi.php | 8 +- tests/spec/OpenApiTest.php | 4 +- 7 files changed, 2288 insertions(+), 14 deletions(-) create mode 100644 schemas/openapi-v3.1.json create mode 100644 schemas/openapi-v3.1.yaml diff --git a/Makefile b/Makefile index 627df52..af7cf6f 100644 --- a/Makefile +++ b/Makefile @@ -36,10 +36,13 @@ stan: php $(PHPARGS) vendor/bin/phpstan analyse -l 5 src # copy openapi3 json schema -schemas/openapi-v3.0.json: vendor/oai/openapi-specification/schemas/v3.0/schema.json +schemas/openapi-v3.0.json: vendor/oai/openapi-specification-3.0/schemas/v3.0/schema.json cp $< $@ - -schemas/openapi-v3.0.yaml: vendor/oai/openapi-specification/schemas/v3.0/schema.yaml +schemas/openapi-v3.0.yaml: vendor/oai/openapi-specification-3.0/schemas/v3.0/schema.yaml + cp $< $@ +schemas/openapi-v3.1.json: vendor/oai/openapi-specification-3.1/schemas/v3.1/schema.json + cp $< $@ +schemas/openapi-v3.1.yaml: vendor/oai/openapi-specification-3.1/schemas/v3.1/schema.yaml cp $< $@ php-cs-fixer.phar: diff --git a/bin/php-openapi b/bin/php-openapi index ae2ffdc..53e5196 100755 --- a/bin/php-openapi +++ b/bin/php-openapi @@ -131,16 +131,22 @@ switch ($command) { // Validate + // OpenAPI version check + $openApiVersion = $openApi->getMajorVersion(); + if ($openApiVersion === \cebe\openapi\spec\OpenApi::VERSION_UNSUPPORTED) { + error("Unsupported OpenAPI version: " . $openApi->openapi); + } + $openApi->validate(); $errors = array_merge($errors, $openApi->getErrors()); $validator = new JsonSchema\Validator; $openApiData = $openApi->getSerializableData(); - $validator->validate($openApiData, (object)['$ref' => 'file://' . dirname(__DIR__) . '/schemas/openapi-v3.0.json']); + $validator->validate($openApiData, (object)['$ref' => 'file://' . dirname(__DIR__) . "/schemas/openapi-v{$openApiVersion}.json"]); if ($validator->isValid() && empty($errors)) { if(!$silentMode) { - print_formatted("The supplied API Description \B\Gvalidates\C against the OpenAPI v3.0 schema.\n", STDERR); + print_formatted("The supplied API Description \B\Gvalidates\C against the OpenAPI v{$openApiVersion} schema.\n", STDERR); } exit(0); } @@ -163,7 +169,7 @@ switch ($command) { } } if (!$validator->isValid()) { - print_formatted("\BOpenAPI v3.0 schema violations:\C\n", STDERR); + print_formatted("\BOpenAPI v{$openApiVersion} schema violations:\C\n", STDERR); $errors = $validator->getErrors(); foreach ($errors as $error) { // hide some errors triggered by other errors further down the path diff --git a/composer.json b/composer.json index 941ca3e..6b4ebbe 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,9 @@ "cebe/indent": "*", "phpunit/phpunit": "^6.5 || ^7.5 || ^8.5 || ^9.4", - "oai/openapi-specification": "3.0.3", + "oai/openapi-specification-3.0": "3.0.3", + "oai/openapi-specification-3.1": "3.1.0", + "mermade/openapi3-examples": "1.0.0", "apis-guru/openapi-directory": "1.0.0", "nexmo/api-specification": "1.0.0", @@ -50,7 +52,7 @@ { "type": "package", "package": { - "name": "oai/openapi-specification", + "name": "oai/openapi-specification-3.0", "version": "3.0.3", "source": { "url": "https://github.com/OAI/OpenAPI-Specification", @@ -59,6 +61,18 @@ } } }, + { + "type": "package", + "package": { + "name": "oai/openapi-specification-3.1", + "version": "3.1.0", + "source": { + "url": "https://github.com/OAI/OpenAPI-Specification", + "type": "git", + "reference": "v3.1.1-dev" + } + } + }, { "type": "package", "package": { diff --git a/schemas/openapi-v3.1.json b/schemas/openapi-v3.1.json new file mode 100644 index 0000000..fa987c1 --- /dev/null +++ b/schemas/openapi-v3.1.json @@ -0,0 +1,1340 @@ +{ + "$id": "https://spec.openapis.org/oas/3.1/schema/2021-03-02", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.1\\.\\d+(-.+)?$" + }, + "info": { + "$ref": "#/$defs/info" + }, + "jsonSchemaDialect": { + "$ref": "#/$defs/uri", + "default": "https://spec.openapis.org/oas/3.1/dialect/base" + }, + "servers": { + "$ref": "#/$defs/server" + }, + "paths": { + "$ref": "#/$defs/paths" + }, + "webhooks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "components": { + "$ref": "#/$defs/components" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/$defs/tag" + } + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "openapi", + "info" + ], + "anyOf": [ + { + "required": [ + "paths" + ] + }, + { + "required": [ + "components" + ] + }, + { + "required": [ + "webhooks" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "info": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string" + }, + "contact": { + "$ref": "#/$defs/contact" + }, + "license": { + "$ref": "#/$defs/license" + }, + "version": { + "type": "string" + } + }, + "required": [ + "title", + "version" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "contact": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "email": { + "type": "string" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "license": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "url": { + "$ref": "#/$defs/uri" + } + }, + "required": [ + "name" + ], + "oneOf": [ + { + "required": [ + "identifier" + ] + }, + { + "required": [ + "url" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server": { + "type": "object", + "properties": { + "url": { + "$ref": "#/$defs/uri" + }, + "description": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/server-variable" + } + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server-variable": { + "type": "object", + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "default": { + "type": "string" + }, + "descriptions": { + "type": "string" + } + }, + "required": [ + "default" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "components": { + "type": "object", + "properties": { + "schemas": { + "type": "object", + "additionalProperties": { + "$dynamicRef": "#meta" + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/response-or-reference" + } + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + }, + "requestBodies": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/request-body-or-reference" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "securitySchemes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/security-scheme-or-reference" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "pathItems": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + } + }, + "patternProperties": { + "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { + "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", + "propertyNames": { + "pattern": "^[a-zA-Z0-9._-]+$" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "paths": { + "type": "object", + "patternProperties": { + "^/": { + "$ref": "#/$defs/path-item" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item": { + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + } + }, + "patternProperties": { + "^(get|post|delete|options|head|patch|trace)$": { + "$ref": "#/$defs/operation" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/path-item" + } + }, + "operation": { + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "requestBody": { + "$ref": "#/$defs/request-body-or-reference" + }, + "responses": { + "$ref": "#/$defs/responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "external-documentation": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "url": { + "$ref": "#/$defs/uri" + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "path", + "cookie" + ] + }, + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "allowEmptyValue": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content" + } + }, + "required": [ + "in" + ], + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-form" + } + ], + "$defs": { + "styles-for-path": { + "if": { + "properties": { + "in": { + "const": "path" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "enum": [ + "matrix", + "label", + "simple" + ] + }, + "required": { + "const": true + } + }, + "required": [ + "required" + ] + } + }, + "styles-for-header": { + "if": { + "properties": { + "in": { + "const": "header" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "enum": [ + "simple" + ] + } + } + } + }, + "styles-for-query": { + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + } + } + } + }, + "styles-for-cookie": { + "if": { + "properties": { + "in": { + "const": "cookie" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form" + ] + } + } + } + }, + "styles-for-form": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/parameter" + } + }, + "request-body": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "content": { + "$ref": "#/$defs/content" + }, + "required": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "request-body-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/request-body" + } + }, + "content": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type" + }, + "propertyNames": { + "format": "media-range" + } + }, + "media-type": { + "type": "object", + "properties": { + "schema": { + "$dynamicRef": "#meta" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/examples" + } + ], + "unevaluatedProperties": false + }, + "encoding": { + "type": "object", + "properties": { + "contentType": { + "type": "string", + "format": "media-range" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/encoding/$defs/explode-default" + } + ], + "unevaluatedProperties": false, + "$defs": { + "explode-default": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } + }, + "responses": { + "type": "object", + "properties": { + "default": { + "$ref": "#/$defs/response-or-reference" + } + }, + "patternProperties": { + "^[1-5][0-9X]{2}$": { + "$ref": "#/$defs/response-or-reference" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "content": { + "$ref": "#/$defs/content" + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + } + }, + "required": [ + "description" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/response" + } + }, + "callbacks": { + "type": "object", + "$ref": "#/$defs/specification-extensions", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "callbacks-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/callbacks" + } + }, + "example": { + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": true, + "externalValue": { + "$ref": "#/$defs/uri" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "example-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/example" + } + }, + "link": { + "type": "object", + "properties": { + "operationRef": { + "$ref": "#/$defs/uri" + }, + "operationId": true, + "parameters": { + "$ref": "#/$defs/map-of-strings" + }, + "requestBody": true, + "description": { + "type": "string" + }, + "body": { + "$ref": "#/$defs/server" + } + }, + "oneOf": [ + { + "required": [ + "operationRef" + ] + }, + { + "required": [ + "operationId" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "link-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/link" + } + }, + "header": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "allowEmptyValue": { + "default": false, + "type": "boolean" + } + }, + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "default": "simple", + "enum": [ + "simple" + ] + }, + "explode": { + "default": false, + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + } + }, + "$ref": "#/$defs/examples" + }, + "content": { + "properties": { + "content": { + "$ref": "#/$defs/content" + } + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "header-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/header" + } + }, + "tag": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "name" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "reference": { + "type": "object", + "properties": { + "$ref": { + "$ref": "#/$defs/uri" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "unevaluatedProperties": false + }, + "schema": { + "$dynamicAnchor": "meta", + "type": [ + "object", + "boolean" + ] + }, + "security-scheme": { + "type": "object", + "properties": { + "type": { + "enum": [ + "apiKey", + "http", + "mutualTLS", + "oauth2", + "openIdConnect" + ] + }, + "description": { + "type": "string" + } + }, + "required": [ + "type" + ], + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-apikey" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oauth2" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oidc" + } + ], + "unevaluatedProperties": false, + "$defs": { + "type-apikey": { + "if": { + "properties": { + "type": { + "const": "apiKey" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "cookie" + ] + } + }, + "required": [ + "name", + "in" + ] + } + }, + "type-http": { + "if": { + "properties": { + "type": { + "const": "http" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "scheme": { + "type": "string" + } + }, + "required": [ + "scheme" + ] + } + }, + "type-http-bearer": { + "if": { + "properties": { + "type": { + "const": "http" + }, + "scheme": { + "const": "bearer" + } + }, + "required": [ + "type", + "scheme" + ] + }, + "then": { + "properties": { + "bearerFormat": { + "type": "string" + } + }, + "required": [ + "scheme" + ] + } + }, + "type-oauth2": { + "if": { + "properties": { + "type": { + "const": "oauth2" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "flows": { + "$ref": "#/$defs/oauth-flows" + } + }, + "required": [ + "flows" + ] + } + }, + "type-oidc": { + "if": { + "properties": { + "type": { + "const": "openIdConnect" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "openIdConnectUrl": { + "$ref": "#/$defs/uri" + } + }, + "required": [ + "openIdConnectUrl" + ] + } + } + } + }, + "security-scheme-or-reference": { + "if": { + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/security-scheme" + } + }, + "oauth-flows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/$defs/oauth-flows/$defs/implicit" + }, + "password": { + "$ref": "#/$defs/oauth-flows/$defs/password" + }, + "clientCredentials": { + "$ref": "#/$defs/oauth-flows/$defs/client-credentials" + }, + "authorizationCode": { + "$ref": "#/$defs/oauth-flows/$defs/authorization-code" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "implicit": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string" + }, + "refreshUrl": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "password": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string" + }, + "refreshUrl": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "client-credentials": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string" + }, + "refreshUrl": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "authorization-code": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string" + }, + "tokenUrl": { + "type": "string" + }, + "refreshUrl": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + } + } + }, + "security-requirement": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "specification-extensions": { + "patternProperties": { + "^x-": true + } + }, + "examples": { + "properties": { + "example": true, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + } + } + }, + "uri": { + "type": "string", + "format": "uri" + }, + "map-of-strings": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } +} diff --git a/schemas/openapi-v3.1.yaml b/schemas/openapi-v3.1.yaml new file mode 100644 index 0000000..9b71e11 --- /dev/null +++ b/schemas/openapi-v3.1.yaml @@ -0,0 +1,911 @@ +$id: 'https://spec.openapis.org/oas/3.1/schema/2021-03-02' +$schema: 'https://json-schema.org/draft/2020-12/schema' + +type: object +properties: + openapi: + type: string + pattern: '^3\.1\.\d+(-.+)?$' + info: + $ref: '#/$defs/info' + jsonSchemaDialect: + $ref: '#/$defs/uri' + default: 'https://spec.openapis.org/oas/3.1/dialect/base' + servers: + $ref: '#/$defs/server' + paths: + $ref: '#/$defs/paths' + webhooks: + type: object + additionalProperties: + $ref: '#/$defs/path-item-or-reference' + components: + $ref: '#/$defs/components' + security: + type: array + items: + $ref: '#/$defs/security-requirement' + tags: + type: array + items: + $ref: '#/$defs/tag' + externalDocs: + $ref: '#/$defs/external-documentation' +required: + - openapi + - info +anyOf: + - required: + - paths + - required: + - components + - required: + - webhooks +$ref: '#/$defs/specification-extensions' +unevaluatedProperties: false + +$defs: + info: + type: object + properties: + title: + type: string + summary: + type: string + description: + type: string + termsOfService: + type: string + contact: + $ref: '#/$defs/contact' + license: + $ref: '#/$defs/license' + version: + type: string + required: + - title + - version + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + contact: + type: object + properties: + name: + type: string + url: + type: string + email: + type: string + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + license: + type: object + properties: + name: + type: string + identifier: + type: string + url: + $ref: '#/$defs/uri' + required: + - name + oneOf: + - required: + - identifier + - required: + - url + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + server: + type: object + properties: + url: + $ref: '#/$defs/uri' + description: + type: string + variables: + type: object + additionalProperties: + $ref: '#/$defs/server-variable' + required: + - url + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + server-variable: + type: object + properties: + enum: + type: array + items: + type: string + minItems: 1 + default: + type: string + descriptions: + type: string + required: + - default + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + components: + type: object + properties: + schemas: + type: object + additionalProperties: + $dynamicRef: '#meta' + responses: + type: object + additionalProperties: + $ref: '#/$defs/response-or-reference' + parameters: + type: object + additionalProperties: + $ref: '#/$defs/parameter-or-reference' + examples: + type: object + additionalProperties: + $ref: '#/$defs/example-or-reference' + requestBodies: + type: object + additionalProperties: + $ref: '#/$defs/request-body-or-reference' + headers: + type: object + additionalProperties: + $ref: '#/$defs/header-or-reference' + securitySchemes: + type: object + additionalProperties: + $ref: '#/$defs/security-scheme-or-reference' + links: + type: object + additionalProperties: + $ref: '#/$defs/link-or-reference' + callbacks: + type: object + additionalProperties: + $ref: '#/$defs/callbacks-or-reference' + pathItems: + type: object + additionalProperties: + $ref: '#/$defs/path-item-or-reference' + patternProperties: + '^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$': + $comment: Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected + propertyNames: + pattern: '^[a-zA-Z0-9._-]+$' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + paths: + type: object + patternProperties: + '^/': + $ref: '#/$defs/path-item' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + path-item: + type: object + properties: + summary: + type: string + description: + type: string + servers: + type: array + items: + $ref: '#/$defs/server' + parameters: + type: array + items: + $ref: '#/$defs/parameter-or-reference' + patternProperties: + '^(get|post|delete|options|head|patch|trace)$': + $ref: '#/$defs/operation' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + path-item-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/path-item' + + operation: + type: object + properties: + tags: + type: array + items: + type: string + summary: + type: string + description: + type: string + externalDocs: + $ref: '#/$defs/external-documentation' + operationId: + type: string + parameters: + type: array + items: + $ref: '#/$defs/parameter-or-reference' + requestBody: + $ref: '#/$defs/request-body-or-reference' + responses: + $ref: '#/$defs/responses' + callbacks: + type: object + additionalProperties: + $ref: '#/$defs/callbacks-or-reference' + deprecated: + default: false + type: boolean + security: + type: array + items: + $ref: '#/$defs/security-requirement' + servers: + type: array + items: + $ref: '#/$defs/server' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + external-documentation: + type: object + properties: + description: + type: string + url: + $ref: '#/$defs/uri' + required: + - url + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + parameter: + type: object + properties: + name: + type: string + in: + enum: + - query + - header + - path + - cookie + description: + type: string + required: + default: false + type: boolean + deprecated: + default: false + type: boolean + allowEmptyValue: + default: false + type: boolean + schema: + $dynamicRef: '#meta' + content: + $ref: '#/$defs/content' + required: + - in + oneOf: + - required: + - schema + - required: + - content + dependentSchemas: + schema: + properties: + style: + type: string + explode: + type: boolean + allowReserved: + default: false + type: boolean + allOf: + - $ref: '#/$defs/examples' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie' + - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-form' + + $defs: + styles-for-path: + if: + properties: + in: + const: path + required: + - in + then: + properties: + style: + default: simple + enum: + - matrix + - label + - simple + required: + const: true + required: + - required + + styles-for-header: + if: + properties: + in: + const: header + required: + - in + then: + properties: + style: + default: simple + enum: + - simple + + styles-for-query: + if: + properties: + in: + const: query + required: + - in + then: + properties: + style: + default: form + enum: + - form + - spaceDelimited + - pipeDelimited + - deepObject + + styles-for-cookie: + if: + properties: + in: + const: cookie + required: + - in + then: + properties: + style: + default: form + enum: + - form + + styles-for-form: + if: + properties: + style: + const: form + required: + - style + then: + properties: + explode: + default: true + else: + properties: + explode: + default: false + + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + parameter-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/parameter' + + request-body: + type: object + properties: + description: + type: string + content: + $ref: '#/$defs/content' + required: + default: false + type: boolean + required: + - content + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + request-body-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/request-body' + + content: + type: object + additionalProperties: + $ref: '#/$defs/media-type' + propertyNames: + format: media-range + + media-type: + type: object + properties: + schema: + $dynamicRef: '#meta' + encoding: + type: object + additionalProperties: + $ref: '#/$defs/encoding' + allOf: + - $ref: '#/$defs/specification-extensions' + - $ref: '#/$defs/examples' + unevaluatedProperties: false + + encoding: + type: object + properties: + contentType: + type: string + format: media-range + headers: + type: object + additionalProperties: + $ref: '#/$defs/header-or-reference' + style: + default: form + enum: + - form + - spaceDelimited + - pipeDelimited + - deepObject + explode: + type: boolean + allowReserved: + default: false + type: boolean + allOf: + - $ref: '#/$defs/specification-extensions' + - $ref: '#/$defs/encoding/$defs/explode-default' + unevaluatedProperties: false + + $defs: + explode-default: + if: + properties: + style: + const: form + required: + - style + then: + properties: + explode: + default: true + else: + properties: + explode: + default: false + + responses: + type: object + properties: + default: + $ref: '#/$defs/response-or-reference' + patternProperties: + '^[1-5][0-9X]{2}$': + $ref: '#/$defs/response-or-reference' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + response: + type: object + properties: + description: + type: string + headers: + type: object + additionalProperties: + $ref: '#/$defs/header-or-reference' + content: + $ref: '#/$defs/content' + links: + type: object + additionalProperties: + $ref: '#/$defs/link-or-reference' + required: + - description + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + response-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/response' + + callbacks: + type: object + $ref: '#/$defs/specification-extensions' + additionalProperties: + $ref: '#/$defs/path-item-or-reference' + + callbacks-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/callbacks' + + example: + type: object + properties: + summary: + type: string + description: + type: string + value: true + externalValue: + $ref: '#/$defs/uri' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + example-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/example' + + link: + type: object + properties: + operationRef: + $ref: '#/$defs/uri' + operationId: true + parameters: + $ref: '#/$defs/map-of-strings' + requestBody: true + description: + type: string + body: + $ref: '#/$defs/server' + oneOf: + - required: + - operationRef + - required: + - operationId + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + link-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/link' + + header: + type: object + properties: + description: + type: string + required: + default: false + type: boolean + deprecated: + default: false + type: boolean + allowEmptyValue: + default: false + type: boolean + dependentSchemas: + schema: + properties: + style: + default: simple + enum: + - simple + explode: + default: false + type: boolean + allowReserved: + default: false + type: boolean + schema: + $dynamicRef: '#meta' + $ref: '#/$defs/examples' + content: + properties: + content: + $ref: '#/$defs/content' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + header-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/header' + + tag: + type: object + properties: + name: + type: string + description: + type: string + externalDocs: + $ref: '#/$defs/external-documentation' + required: + - name + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + reference: + type: object + properties: + $ref: + $ref: '#/$defs/uri' + summary: + type: string + description: + type: string + unevaluatedProperties: false + + schema: + $dynamicAnchor: meta + type: + - object + - boolean + + security-scheme: + type: object + properties: + type: + enum: + - apiKey + - http + - mutualTLS + - oauth2 + - openIdConnect + description: + type: string + required: + - type + allOf: + - $ref: '#/$defs/specification-extensions' + - $ref: '#/$defs/security-scheme/$defs/type-apikey' + - $ref: '#/$defs/security-scheme/$defs/type-http' + - $ref: '#/$defs/security-scheme/$defs/type-http-bearer' + - $ref: '#/$defs/security-scheme/$defs/type-oauth2' + - $ref: '#/$defs/security-scheme/$defs/type-oidc' + unevaluatedProperties: false + + $defs: + type-apikey: + if: + properties: + type: + const: apiKey + required: + - type + then: + properties: + name: + type: string + in: + enum: + - query + - header + - cookie + required: + - name + - in + + type-http: + if: + properties: + type: + const: http + required: + - type + then: + properties: + scheme: + type: string + required: + - scheme + + type-http-bearer: + if: + properties: + type: + const: http + scheme: + const: bearer + required: + - type + - scheme + then: + properties: + bearerFormat: + type: string + required: + - scheme + + type-oauth2: + if: + properties: + type: + const: oauth2 + required: + - type + then: + properties: + flows: + $ref: '#/$defs/oauth-flows' + required: + - flows + + type-oidc: + if: + properties: + type: + const: openIdConnect + required: + - type + then: + properties: + openIdConnectUrl: + $ref: '#/$defs/uri' + required: + - openIdConnectUrl + + security-scheme-or-reference: + if: + required: + - $ref + then: + $ref: '#/$defs/reference' + else: + $ref: '#/$defs/security-scheme' + + oauth-flows: + type: object + properties: + implicit: + $ref: '#/$defs/oauth-flows/$defs/implicit' + password: + $ref: '#/$defs/oauth-flows/$defs/password' + clientCredentials: + $ref: '#/$defs/oauth-flows/$defs/client-credentials' + authorizationCode: + $ref: '#/$defs/oauth-flows/$defs/authorization-code' + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + $defs: + implicit: + type: object + properties: + authorizationUrl: + type: string + refreshUrl: + type: string + scopes: + $ref: '#/$defs/map-of-strings' + required: + - authorizationUrl + - scopes + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + password: + type: object + properties: + tokenUrl: + type: string + refreshUrl: + type: string + scopes: + $ref: '#/$defs/map-of-strings' + required: + - tokenUrl + - scopes + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + client-credentials: + type: object + properties: + tokenUrl: + type: string + refreshUrl: + type: string + scopes: + $ref: '#/$defs/map-of-strings' + required: + - tokenUrl + - scopes + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + authorization-code: + type: object + properties: + authorizationUrl: + type: string + tokenUrl: + type: string + refreshUrl: + type: string + scopes: + $ref: '#/$defs/map-of-strings' + required: + - authorizationUrl + - tokenUrl + - scopes + $ref: '#/$defs/specification-extensions' + unevaluatedProperties: false + + security-requirement: + type: object + additionalProperties: + type: array + items: + type: string + + specification-extensions: + patternProperties: + '^x-': true + + examples: + properties: + example: true + examples: + type: object + additionalProperties: + $ref: '#/$defs/example-or-reference' + + uri: + type: string + format: uri + + map-of-strings: + type: object + additionalProperties: + type: string diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 1cb4206..c45fbad 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -27,8 +27,8 @@ */ class OpenApi extends SpecBaseObject { - const VERSION_3_0 = '3.0.x'; - const VERSION_3_1 = '3.1.x'; + const VERSION_3_0 = '3.0'; + const VERSION_3_1 = '3.1'; const VERSION_UNSUPPORTED = 'unsupported'; /** @@ -93,8 +93,8 @@ public function performValidation() * Returns the OpenAPI major version of the loaded OpenAPI description. * @return string This returns a value of one of the `VERSION_*`-constants. Currently supported versions are: * - * - `VERSION_3_0 = '3.0.x'` - * - `VERSION_3_1 = '3.1.x'` + * - `VERSION_3_0 = '3.0'` + * - `VERSION_3_1 = '3.1'` * * For unsupported version, this function will return `VERSION_UNSUPPORTED = 'unsupported'` */ diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index 7732040..2142319 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -261,7 +261,7 @@ public function testVersions() YAML; $openapi = Reader::readFromYaml($yaml); $this->assertTrue($openapi->validate(), print_r($openapi->getErrors(), true)); - $this->assertEquals('3.0.x', $openapi->getMajorVersion()); + $this->assertEquals('3.0', $openapi->getMajorVersion()); $yaml = <<assertTrue($openapi->validate(), print_r($openapi->getErrors(), true)); - $this->assertEquals('3.1.x', $openapi->getMajorVersion()); + $this->assertEquals('3.1', $openapi->getMajorVersion()); } From eb1108d63fcb0867219366f9c52bed9071294dbd Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 18 Oct 2021 15:25:59 +0200 Subject: [PATCH 3/8] fix test paths --- tests/ReaderTest.php | 2 +- tests/spec/OpenApiTest.php | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ReaderTest.php b/tests/ReaderTest.php index 6e961ab..561c9e3 100644 --- a/tests/ReaderTest.php +++ b/tests/ReaderTest.php @@ -108,7 +108,7 @@ public function testSymfonyYamlBugHunt() } } - $openApiFile = __DIR__ . '/../vendor/oai/openapi-specification/examples/v3.0/uspto.yaml'; + $openApiFile = __DIR__ . '/../vendor/oai/openapi-specification-3.0/examples/v3.0/uspto.yaml'; $openapi = \cebe\openapi\Reader::readFromYamlFile($openApiFile); $inlineYamlExample = $openapi->paths['/']->get->responses['200']->content['application/json']->example; diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index 2142319..b2915e7 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -29,7 +29,7 @@ public function testEmpty() public function testReadPetStore() { - $openApiFile = __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/petstore.yaml'; + $openApiFile = __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/petstore.yaml'; $yaml = Yaml::parse(file_get_contents($openApiFile)); $openapi = new OpenApi($yaml); @@ -94,12 +94,12 @@ public function specProvider() // examples from https://github.com/OAI/OpenAPI-Specification/tree/master/examples/v3.0 $oaiExamples = [ // TODO symfony/yaml can not read this file!? -// __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/api-with-examples.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/callback-example.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/link-example.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/petstore.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/petstore-expanded.yaml', - __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/uspto.yaml', +// __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/api-with-examples.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/callback-example.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/link-example.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/petstore.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/petstore-expanded.yaml', + __DIR__ . '/../../vendor/oai/openapi-specification-3.0/examples/v3.0/uspto.yaml', ]; // examples from https://github.com/Mermade/openapi3-examples From 4fcc155846b79594763e37dde5e6d239b71041ff Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 18 Oct 2021 15:33:23 +0200 Subject: [PATCH 4/8] Update README.md --- README.md | 53 ++++++++--------------------------------------------- 1 file changed, 8 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index fd1aedf..88a2340 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,13 @@ # php-openapi -Read and write [OpenAPI](https://www.openapis.org/) 3.0.x YAML and JSON files and make the content accessible in PHP objects. +Read and write [OpenAPI](https://www.openapis.org/) 3.x YAML and JSON files and make the content accessible in PHP objects. -It also provides a CLI tool for validating and converting OpenAPI 3.0.x Description files. +It also provides a CLI tool for validating and converting OpenAPI 3.x Description files. + +Supported OpenAPI versions: + +- 3.0.x +- 3.1.x [![Latest Stable Version](https://poser.pugx.org/cebe/php-openapi/v/stable)](https://packagist.org/packages/cebe/php-openapi) [![Total Downloads](https://poser.pugx.org/cebe/php-openapi/downloads)](https://packagist.org/packages/cebe/php-openapi) @@ -212,7 +217,7 @@ $openapi->resolveReferences( The library provides simple validation operations, that check basic OpenAPI spec requirements. This is the same as "structural errors found while reading the API Description file" from the CLI tool. -This validation does not include checking against the OpenAPI v3.0 JSON schema, this is only implemented in the CLI. +This validation does not include checking against the OpenAPI v3.0/v3.1 JSON schemas, this is only implemented in the CLI. ``` // return `true` in case no errors have been found, `false` in case of errors. @@ -225,48 +230,6 @@ $errors = $openapi->getErrors(); > but the list of errors given may not be complete. Also a passing validation does not necessarily indicate a completely > valid spec. - -## Completeness - -This library is currently work in progress, the following list tracks completeness: - -- [x] read OpenAPI 3.0 JSON -- [x] read OpenAPI 3.0 YAML -- [ ] OpenAPI 3.0 Schema - - [x] OpenAPI Object - - [x] Info Object - - [x] Contact Object - - [x] License Object - - [x] Server Object - - [x] Server Variable Object - - [x] Components Object - - [x] Paths Object - - [x] Path Item Object - - [x] Operation Object - - [x] External Documentation Object - - [x] Parameter Object - - [x] Request Body Object - - [x] Media Type Object - - [x] Encoding Object - - [x] Responses Object - - [x] Response Object - - [x] Callback Object - - [x] Example Object - - [x] Link Object - - [ ] [Runtime Expressions](https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#runtime-expressions) - - [x] Header Object - - [x] Tag Object - - [x] Reference Object - - [x] Schema Object - - [x] load/read - - [ ] validation - - [x] Discriminator Object - - [x] XML Object - - [x] Security Scheme Object - - [x] OAuth Flows Object - - [x] OAuth Flow Object - - [x] Security Requirement Object - # Support **Need help with your API project?** From c4bba495a8acab9fad37f5b99cfebf71d0206751 Mon Sep 17 00:00:00 2001 From: Marcel Thole Date: Wed, 20 Oct 2021 14:44:32 +0200 Subject: [PATCH 5/8] Add Testcases for OpenAPI 3.1 --- composer.json | 2 +- tests/spec/OpenApiTest.php | 84 ++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/composer.json b/composer.json index 6b4ebbe..7225443 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,7 @@ "source": { "url": "https://github.com/Mermade/openapi3-examples", "type": "git", - "reference": "3e8740c4994310a5d6a35d9b19e405862326f149" + "reference": "9c2997e1a25919a8182080cc43a4db06d2dc775d" } } }, diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index b2915e7..2cb4024 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -104,43 +104,47 @@ public function specProvider() // examples from https://github.com/Mermade/openapi3-examples $mermadeExamples = [ - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/externalPathItemRef.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/deprecated.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/swagger2openapi/openapi.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Fixed_file.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Fixed_file.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Fixed_multipart.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Improved_examples.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Improved_pathdescriptions.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Improved_securityschemes.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._Improved_serverseverywhere.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._New_callbacks.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example1_from_._New_links.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Different_requestbody.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Different_servers.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Fixed_multipart.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._Improved_securityschemes.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._New_callbacks.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example2_from_._New_links.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example3_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example3_from_._Different_servers.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example4_from_._Different_parameters.md.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/gluecon/example5_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/externalPathItemRef.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/deprecated.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/swagger2openapi/openapi.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Fixed_file.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Fixed_multipart.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Improved_examples.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Improved_pathdescriptions.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Improved_securityschemes.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._Improved_serverseverywhere.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._New_callbacks.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example1_from_._New_links.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Different_requestbody.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Different_servers.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Fixed_multipart.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._Improved_securityschemes.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._New_callbacks.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example2_from_._New_links.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example3_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example3_from_._Different_servers.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example4_from_._Different_parameters.md.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/gluecon/example5_from_._Different_parameters.md.yaml', // TODO symfony/yaml can not read this file!? -// __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/OAI/api-with-examples.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/OAI/petstore-expanded.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/OAI/petstore.yaml', - __DIR__ . '/../../vendor/mermade/openapi3-examples/pass/OAI/uber.yaml', - - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-html.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-java.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-js.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-php.json', - __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/rapid7-ruby.json', -// __DIR__ . '/../../vendor/mermade/openapi3-examples/malicious/yamlbomb.yaml', +// __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/OAI/api-with-examples.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/OAI/petstore-expanded.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/OAI/petstore.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/pass/OAI/uber.yaml', + + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-html.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-java.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-js.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-php.json', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/rapid7-ruby.json', +// __DIR__ . '/../../vendor/mermade/openapi3-examples/3.0/malicious/yamlbomb.yaml', + + // OpenAPI 3.1 examples + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.1/pass/minimal_comp.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.1/pass/minimal_hooks.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.1/pass/minimal_paths.yaml', + __DIR__ . '/../../vendor/mermade/openapi3-examples/3.1/pass/path_var_empty_pathitem.yaml', ]; // examples from https://github.com/APIs-guru/openapi-directory/tree/openapi3.0.0/APIs @@ -177,9 +181,9 @@ public function specProvider() $nexmoExamples ); foreach($all as $path) { - yield [ - substr($path, strlen(__DIR__ . '/../../vendor/')), - basename(dirname($path, 2)) . DIRECTORY_SEPARATOR . basename(dirname($path, 1)) . DIRECTORY_SEPARATOR . basename($path) + $pathWithoutVendorPrefix = substr($path, strlen(__DIR__ . '/../../vendor/')); + yield $pathWithoutVendorPrefix => [ + $pathWithoutVendorPrefix ]; } } @@ -219,7 +223,7 @@ public function testSpecs($openApiFile) $this->assertTrue($result); // openapi - $this->assertStringStartsWith('3.0.', $openapi->openapi); + $this->assertNotSame(OpenApi::VERSION_UNSUPPORTED, $openapi->getMajorVersion()); // info $this->assertInstanceOf(\cebe\openapi\spec\Info::class, $openapi->info); From 2cb3b0d7654bea59d4240b79d229105d6d95479b Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 28 Oct 2021 09:14:51 +0200 Subject: [PATCH 6/8] Update php.yml --- .github/workflows/php.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index ccd13c2..e0b786a 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -2,9 +2,9 @@ name: PHP Composer on: push: - branches: [ master ] + branches: [ master, openapi-31 ] pull_request: - branches: [ master ] + branches: [ master, openapi-31 ] jobs: build: From 0c10bea92d8144d3d2bf9379898f9a0a54451923 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 1 Jun 2021 19:22:53 +0200 Subject: [PATCH 7/8] Add support for WebHooks --- src/SpecBaseObject.php | 12 +++++++++++- src/spec/OpenApi.php | 9 ++++++++- src/spec/Schema.php | 2 +- src/spec/Type.php | 2 ++ tests/spec/OpenApiTest.php | 9 +++++++-- tests/spec/PathTest.php | 2 +- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 29f70b4..6e87c1f 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -302,13 +302,23 @@ protected function hasProperty(string $name): bool return isset($this->_properties[$name]) || isset($this->attributes()[$name]); } - protected function requireProperties(array $names) + protected function requireProperties(array $names, array $atLeastOne = []) { foreach ($names as $name) { if (!isset($this->_properties[$name])) { $this->addError(" is missing required property: $name", get_class($this)); } } + + if (count($atLeastOne) > 0) { + foreach ($atLeastOne as $name) { + if (array_key_exists($name, $this->_properties)) { + return; + } + } + + $this->addError(" is missing at least one of the following required properties: " . implode(', ', $atLeastOne), get_class($this)); + } } protected function validateEmail(string $property) diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index c45fbad..7476ce8 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -20,6 +20,7 @@ * @property Server[] $servers * @property Paths|PathItem[] $paths * @property Components|null $components + * @property PathItem[]|null $webhooks * @property SecurityRequirement[] $security * @property Tag[] $tags * @property ExternalDocumentation|null $externalDocs @@ -46,6 +47,7 @@ protected function attributes(): array 'info' => Info::class, 'servers' => [Server::class], 'paths' => Paths::class, + 'webhooks' => [PathItem::class], 'components' => Components::class, 'security' => [SecurityRequirement::class], 'tags' => [Tag::class], @@ -83,7 +85,12 @@ public function __get($name) */ public function performValidation() { - $this->requireProperties(['openapi', 'info', 'paths']); + if ($this->getMajorVersion() === static::VERSION_3_0) { + $this->requireProperties(['openapi', 'info', 'paths']); + } else { + $this->requireProperties(['openapi', 'info'], ['paths', 'webhooks', 'components']); + } + if (!empty($this->openapi) && !preg_match(static::PATTERN_VERSION, $this->openapi)) { $this->addError('Unsupported openapi version: ' . $this->openapi); } diff --git a/src/spec/Schema.php b/src/spec/Schema.php index 59eb425..5e96230 100644 --- a/src/spec/Schema.php +++ b/src/spec/Schema.php @@ -40,7 +40,7 @@ * @property string[] $required list of required properties * @property array $enum * - * @property string $type + * @property string|string[] $type type can only be `string` in OpenAPI 3.0, but can be an array of strings since OpenAPI 3.1 * @property Schema[]|Reference[] $allOf * @property Schema[]|Reference[] $oneOf * @property Schema[]|Reference[] $anyOf diff --git a/src/spec/Type.php b/src/spec/Type.php index 9861000..b4c8123 100644 --- a/src/spec/Type.php +++ b/src/spec/Type.php @@ -21,6 +21,7 @@ class Type const BOOLEAN = 'boolean'; const OBJECT = 'object'; const ARRAY = 'array'; + const NULL = 'null'; // Since OpenAPI 3.1 /** * Indicate whether a type is a scalar type, i.e. not an array or object. @@ -38,6 +39,7 @@ public static function isScalar(string $type): bool self::NUMBER, self::STRING, self::BOOLEAN, + self::NULL, ]); } } diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index 2cb4024..99b3346 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -17,7 +17,7 @@ public function testEmpty() $this->assertEquals([ 'OpenApi is missing required property: openapi', 'OpenApi is missing required property: info', - 'OpenApi is missing required property: paths', + 'OpenApi is missing at least one of the following required properties: paths, webhooks, components', ], $openapi->getErrors()); // check default value of servers @@ -232,10 +232,15 @@ public function testSpecs($openApiFile) $this->assertAllInstanceOf(\cebe\openapi\spec\Server::class, $openapi->servers); // paths - if ($openapi->components !== null) { + if ($openapi->paths !== null) { $this->assertInstanceOf(\cebe\openapi\spec\Paths::class, $openapi->paths); } + // webhooks + if ($openapi->webhooks !== null) { + $this->assertAllInstanceOf(\cebe\openapi\spec\PathItem::class, $openapi->webhooks); + } + // components if ($openapi->components !== null) { $this->assertInstanceOf(\cebe\openapi\spec\Components::class, $openapi->components); diff --git a/tests/spec/PathTest.php b/tests/spec/PathTest.php index 6c46e4b..d028258 100644 --- a/tests/spec/PathTest.php +++ b/tests/spec/PathTest.php @@ -66,7 +66,7 @@ public function testRead() } } - public function testCreateionFromObjects() + public function testCreationFromObjects() { $paths = new Paths([ '/pets' => new PathItem([ From c88cb31574989c6f69ca85debc95d4251d69f931 Mon Sep 17 00:00:00 2001 From: Aleksei Khudiakov Date: Wed, 9 Feb 2022 21:31:42 +1000 Subject: [PATCH 8/8] Update pre-release openapi 3.1 schema to current (#140) Signed-off-by: Aleksei Khudiakov --- schemas/openapi-v3.1.json | 141 +++++++++++++++++++++++++------------- schemas/openapi-v3.1.yaml | 118 +++++++++++++++++++++---------- 2 files changed, 172 insertions(+), 87 deletions(-) diff --git a/schemas/openapi-v3.1.json b/schemas/openapi-v3.1.json index fa987c1..295029f 100644 --- a/schemas/openapi-v3.1.json +++ b/schemas/openapi-v3.1.json @@ -1,5 +1,5 @@ { - "$id": "https://spec.openapis.org/oas/3.1/schema/2021-03-02", + "$id": "https://spec.openapis.org/oas/3.1/schema/2021-09-28", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { @@ -11,11 +11,15 @@ "$ref": "#/$defs/info" }, "jsonSchemaDialect": { - "$ref": "#/$defs/uri", + "type": "string", + "format": "uri", "default": "https://spec.openapis.org/oas/3.1/dialect/base" }, "servers": { - "$ref": "#/$defs/server" + "type": "array", + "items": { + "$ref": "#/$defs/server" + } }, "paths": { "$ref": "#/$defs/paths" @@ -70,6 +74,7 @@ "unevaluatedProperties": false, "$defs": { "info": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#info-object", "type": "object", "properties": { "title": { @@ -102,6 +107,7 @@ "unevaluatedProperties": false }, "contact": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#contact-object", "type": "object", "properties": { "name": { @@ -118,6 +124,7 @@ "unevaluatedProperties": false }, "license": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#license-object", "type": "object", "properties": { "name": { @@ -127,7 +134,8 @@ "type": "string" }, "url": { - "$ref": "#/$defs/uri" + "type": "string", + "format": "uri" } }, "required": [ @@ -149,10 +157,12 @@ "unevaluatedProperties": false }, "server": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-object", "type": "object", "properties": { "url": { - "$ref": "#/$defs/uri" + "type": "string", + "format": "uri-reference" }, "description": { "type": "string" @@ -171,6 +181,7 @@ "unevaluatedProperties": false }, "server-variable": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-variable-object", "type": "object", "properties": { "enum": { @@ -183,7 +194,7 @@ "default": { "type": "string" }, - "descriptions": { + "description": { "type": "string" } }, @@ -194,6 +205,7 @@ "unevaluatedProperties": false }, "components": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#components-object", "type": "object", "properties": { "schemas": { @@ -269,6 +281,7 @@ "unevaluatedProperties": false }, "paths": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object", "type": "object", "patternProperties": { "^/": { @@ -279,6 +292,7 @@ "unevaluatedProperties": false }, "path-item": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", "type": "object", "properties": { "summary": { @@ -301,7 +315,7 @@ } }, "patternProperties": { - "^(get|post|delete|options|head|patch|trace)$": { + "^(get|put|post|delete|options|head|patch|trace)$": { "$ref": "#/$defs/operation" } }, @@ -310,6 +324,7 @@ }, "path-item-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -322,6 +337,7 @@ } }, "operation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", "type": "object", "properties": { "tags": { @@ -381,13 +397,15 @@ "unevaluatedProperties": false }, "external-documentation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#external-documentation-object", "type": "object", "properties": { "description": { "type": "string" }, "url": { - "$ref": "#/$defs/uri" + "type": "string", + "format": "uri" } }, "required": [ @@ -397,6 +415,7 @@ "unevaluatedProperties": false }, "parameter": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#parameter-object", "type": "object", "properties": { "name": { @@ -429,10 +448,13 @@ "$dynamicRef": "#meta" }, "content": { - "$ref": "#/$defs/content" + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 } }, "required": [ + "name", "in" ], "oneOf": [ @@ -495,6 +517,9 @@ }, "then": { "properties": { + "name": { + "pattern": "[^/#?]+$" + }, "style": { "default": "simple", "enum": [ @@ -527,9 +552,7 @@ "properties": { "style": { "default": "simple", - "enum": [ - "simple" - ] + "const": "simple" } } } @@ -574,9 +597,7 @@ "properties": { "style": { "default": "form", - "enum": [ - "form" - ] + "const": "form" } } } @@ -615,6 +636,7 @@ }, "parameter-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -627,6 +649,7 @@ } }, "request-body": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#request-body-object", "type": "object", "properties": { "description": { @@ -648,6 +671,7 @@ }, "request-body-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -660,6 +684,7 @@ } }, "content": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#fixed-fields-10", "type": "object", "additionalProperties": { "$ref": "#/$defs/media-type" @@ -669,6 +694,7 @@ } }, "media-type": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#media-type-object", "type": "object", "properties": { "schema": { @@ -692,6 +718,7 @@ "unevaluatedProperties": false }, "encoding": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#encoding-object", "type": "object", "properties": { "contentType": { @@ -760,6 +787,7 @@ } }, "responses": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object", "type": "object", "properties": { "default": { @@ -767,7 +795,7 @@ } }, "patternProperties": { - "^[1-5][0-9X]{2}$": { + "^[1-5](?:[0-9]{2}|XX)$": { "$ref": "#/$defs/response-or-reference" } }, @@ -775,6 +803,7 @@ "unevaluatedProperties": false }, "response": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#response-object", "type": "object", "properties": { "description": { @@ -804,6 +833,7 @@ }, "response-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -816,6 +846,7 @@ } }, "callbacks": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#callback-object", "type": "object", "$ref": "#/$defs/specification-extensions", "additionalProperties": { @@ -824,6 +855,7 @@ }, "callbacks-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -836,6 +868,7 @@ } }, "example": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#example-object", "type": "object", "properties": { "summary": { @@ -846,7 +879,8 @@ }, "value": true, "externalValue": { - "$ref": "#/$defs/uri" + "type": "string", + "format": "uri" } }, "$ref": "#/$defs/specification-extensions", @@ -854,6 +888,7 @@ }, "example-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -866,10 +901,12 @@ } }, "link": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#link-object", "type": "object", "properties": { "operationRef": { - "$ref": "#/$defs/uri" + "type": "string", + "format": "uri-reference" }, "operationId": true, "parameters": { @@ -900,6 +937,7 @@ }, "link-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -912,6 +950,7 @@ } }, "header": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#header-object", "type": "object", "properties": { "description": { @@ -925,40 +964,40 @@ "default": false, "type": "boolean" }, - "allowEmptyValue": { - "default": false, - "type": "boolean" + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 } }, + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], "dependentSchemas": { "schema": { "properties": { "style": { "default": "simple", - "enum": [ - "simple" - ] + "const": "simple" }, "explode": { "default": false, "type": "boolean" - }, - "allowReserved": { - "default": false, - "type": "boolean" - }, - "schema": { - "$dynamicRef": "#meta" } }, "$ref": "#/$defs/examples" - }, - "content": { - "properties": { - "content": { - "$ref": "#/$defs/content" - } - } } }, "$ref": "#/$defs/specification-extensions", @@ -966,6 +1005,7 @@ }, "header-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -978,6 +1018,7 @@ } }, "tag": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#tag-object", "type": "object", "properties": { "name": { @@ -997,10 +1038,12 @@ "unevaluatedProperties": false }, "reference": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#reference-object", "type": "object", "properties": { "$ref": { - "$ref": "#/$defs/uri" + "type": "string", + "format": "uri-reference" }, "summary": { "type": "string" @@ -1012,6 +1055,7 @@ "unevaluatedProperties": false }, "schema": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", "$dynamicAnchor": "meta", "type": [ "object", @@ -1019,6 +1063,7 @@ ] }, "security-scheme": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", "type": "object", "properties": { "type": { @@ -1118,7 +1163,8 @@ "const": "http" }, "scheme": { - "const": "bearer" + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" } }, "required": [ @@ -1131,10 +1177,7 @@ "bearerFormat": { "type": "string" } - }, - "required": [ - "scheme" - ] + } } }, "type-oauth2": { @@ -1173,7 +1216,8 @@ "then": { "properties": { "openIdConnectUrl": { - "$ref": "#/$defs/uri" + "type": "string", + "format": "uri" } }, "required": [ @@ -1185,6 +1229,7 @@ }, "security-scheme-or-reference": { "if": { + "type": "object", "required": [ "$ref" ] @@ -1302,6 +1347,7 @@ } }, "security-requirement": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-requirement-object", "type": "object", "additionalProperties": { "type": "array", @@ -1311,6 +1357,7 @@ } }, "specification-extensions": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#specification-extensions", "patternProperties": { "^x-": true } @@ -1326,10 +1373,6 @@ } } }, - "uri": { - "type": "string", - "format": "uri" - }, "map-of-strings": { "type": "object", "additionalProperties": { diff --git a/schemas/openapi-v3.1.yaml b/schemas/openapi-v3.1.yaml index 9b71e11..49c7a52 100644 --- a/schemas/openapi-v3.1.yaml +++ b/schemas/openapi-v3.1.yaml @@ -1,4 +1,4 @@ -$id: 'https://spec.openapis.org/oas/3.1/schema/2021-03-02' +$id: 'https://spec.openapis.org/oas/3.1/schema/2021-09-28' $schema: 'https://json-schema.org/draft/2020-12/schema' type: object @@ -9,10 +9,13 @@ properties: info: $ref: '#/$defs/info' jsonSchemaDialect: - $ref: '#/$defs/uri' + type: string + format: uri default: 'https://spec.openapis.org/oas/3.1/dialect/base' servers: - $ref: '#/$defs/server' + type: array + items: + $ref: '#/$defs/server' paths: $ref: '#/$defs/paths' webhooks: @@ -46,6 +49,7 @@ unevaluatedProperties: false $defs: info: + $comment: https://spec.openapis.org/oas/v3.1.0#info-object type: object properties: title: @@ -69,6 +73,7 @@ $defs: unevaluatedProperties: false contact: + $comment: https://spec.openapis.org/oas/v3.1.0#contact-object type: object properties: name: @@ -81,6 +86,7 @@ $defs: unevaluatedProperties: false license: + $comment: https://spec.openapis.org/oas/v3.1.0#license-object type: object properties: name: @@ -88,7 +94,8 @@ $defs: identifier: type: string url: - $ref: '#/$defs/uri' + type: string + format: uri required: - name oneOf: @@ -100,10 +107,12 @@ $defs: unevaluatedProperties: false server: + $comment: https://spec.openapis.org/oas/v3.1.0#server-object type: object properties: url: - $ref: '#/$defs/uri' + type: string + format: uri-reference description: type: string variables: @@ -116,6 +125,7 @@ $defs: unevaluatedProperties: false server-variable: + $comment: https://spec.openapis.org/oas/v3.1.0#server-variable-object type: object properties: enum: @@ -125,7 +135,7 @@ $defs: minItems: 1 default: type: string - descriptions: + description: type: string required: - default @@ -133,6 +143,7 @@ $defs: unevaluatedProperties: false components: + $comment: https://spec.openapis.org/oas/v3.1.0#components-object type: object properties: schemas: @@ -184,6 +195,7 @@ $defs: unevaluatedProperties: false paths: + $comment: https://spec.openapis.org/oas/v3.1.0#paths-object type: object patternProperties: '^/': @@ -192,6 +204,7 @@ $defs: unevaluatedProperties: false path-item: + $comment: https://spec.openapis.org/oas/v3.1.0#path-item-object type: object properties: summary: @@ -207,13 +220,14 @@ $defs: items: $ref: '#/$defs/parameter-or-reference' patternProperties: - '^(get|post|delete|options|head|patch|trace)$': + '^(get|put|post|delete|options|head|patch|trace)$': $ref: '#/$defs/operation' $ref: '#/$defs/specification-extensions' unevaluatedProperties: false path-item-or-reference: if: + type: object required: - $ref then: @@ -222,6 +236,7 @@ $defs: $ref: '#/$defs/path-item' operation: + $comment: https://spec.openapis.org/oas/v3.1.0#operation-object type: object properties: tags: @@ -263,18 +278,21 @@ $defs: unevaluatedProperties: false external-documentation: + $comment: https://spec.openapis.org/oas/v3.1.0#external-documentation-object type: object properties: description: type: string url: - $ref: '#/$defs/uri' + type: string + format: uri required: - url $ref: '#/$defs/specification-extensions' unevaluatedProperties: false parameter: + $comment: https://spec.openapis.org/oas/v3.1.0#parameter-object type: object properties: name: @@ -300,7 +318,10 @@ $defs: $dynamicRef: '#meta' content: $ref: '#/$defs/content' + minProperties: 1 + maxProperties: 1 required: + - name - in oneOf: - required: @@ -335,6 +356,8 @@ $defs: - in then: properties: + name: + pattern: '[^/#?]+$' style: default: simple enum: @@ -357,8 +380,7 @@ $defs: properties: style: default: simple - enum: - - simple + const: simple styles-for-query: if: @@ -388,8 +410,7 @@ $defs: properties: style: default: form - enum: - - form + const: form styles-for-form: if: @@ -412,6 +433,7 @@ $defs: parameter-or-reference: if: + type: object required: - $ref then: @@ -420,6 +442,7 @@ $defs: $ref: '#/$defs/parameter' request-body: + $comment: https://spec.openapis.org/oas/v3.1.0#request-body-object type: object properties: description: @@ -436,6 +459,7 @@ $defs: request-body-or-reference: if: + type: object required: - $ref then: @@ -444,6 +468,7 @@ $defs: $ref: '#/$defs/request-body' content: + $comment: https://spec.openapis.org/oas/v3.1.0#fixed-fields-10 type: object additionalProperties: $ref: '#/$defs/media-type' @@ -451,6 +476,7 @@ $defs: format: media-range media-type: + $comment: https://spec.openapis.org/oas/v3.1.0#media-type-object type: object properties: schema: @@ -465,6 +491,7 @@ $defs: unevaluatedProperties: false encoding: + $comment: https://spec.openapis.org/oas/v3.1.0#encoding-object type: object properties: contentType: @@ -509,17 +536,19 @@ $defs: default: false responses: + $comment: https://spec.openapis.org/oas/v3.1.0#responses-object type: object properties: default: $ref: '#/$defs/response-or-reference' patternProperties: - '^[1-5][0-9X]{2}$': + '^[1-5](?:[0-9]{2}|XX)$': $ref: '#/$defs/response-or-reference' $ref: '#/$defs/specification-extensions' unevaluatedProperties: false response: + $comment: https://spec.openapis.org/oas/v3.1.0#response-object type: object properties: description: @@ -541,6 +570,7 @@ $defs: response-or-reference: if: + type: object required: - $ref then: @@ -549,6 +579,7 @@ $defs: $ref: '#/$defs/response' callbacks: + $comment: https://spec.openapis.org/oas/v3.1.0#callback-object type: object $ref: '#/$defs/specification-extensions' additionalProperties: @@ -556,6 +587,7 @@ $defs: callbacks-or-reference: if: + type: object required: - $ref then: @@ -564,6 +596,7 @@ $defs: $ref: '#/$defs/callbacks' example: + $comment: https://spec.openapis.org/oas/v3.1.0#example-object type: object properties: summary: @@ -572,12 +605,14 @@ $defs: type: string value: true externalValue: - $ref: '#/$defs/uri' + type: string + format: uri $ref: '#/$defs/specification-extensions' unevaluatedProperties: false example-or-reference: if: + type: object required: - $ref then: @@ -586,10 +621,12 @@ $defs: $ref: '#/$defs/example' link: + $comment: https://spec.openapis.org/oas/v3.1.0#link-object type: object properties: operationRef: - $ref: '#/$defs/uri' + type: string + format: uri-reference operationId: true parameters: $ref: '#/$defs/map-of-strings' @@ -608,6 +645,7 @@ $defs: link-or-reference: if: + type: object required: - $ref then: @@ -616,6 +654,7 @@ $defs: $ref: '#/$defs/link' header: + $comment: https://spec.openapis.org/oas/v3.1.0#header-object type: object properties: description: @@ -626,34 +665,33 @@ $defs: deprecated: default: false type: boolean - allowEmptyValue: - default: false - type: boolean + schema: + $dynamicRef: '#meta' + content: + $ref: '#/$defs/content' + minProperties: 1 + maxProperties: 1 + oneOf: + - required: + - schema + - required: + - content dependentSchemas: schema: properties: style: default: simple - enum: - - simple + const: simple explode: default: false type: boolean - allowReserved: - default: false - type: boolean - schema: - $dynamicRef: '#meta' $ref: '#/$defs/examples' - content: - properties: - content: - $ref: '#/$defs/content' $ref: '#/$defs/specification-extensions' unevaluatedProperties: false header-or-reference: if: + type: object required: - $ref then: @@ -662,6 +700,7 @@ $defs: $ref: '#/$defs/header' tag: + $comment: https://spec.openapis.org/oas/v3.1.0#tag-object type: object properties: name: @@ -676,10 +715,12 @@ $defs: unevaluatedProperties: false reference: + $comment: https://spec.openapis.org/oas/v3.1.0#reference-object type: object properties: $ref: - $ref: '#/$defs/uri' + type: string + format: uri-reference summary: type: string description: @@ -687,12 +728,14 @@ $defs: unevaluatedProperties: false schema: + $comment: https://spec.openapis.org/oas/v3.1.0#schema-object $dynamicAnchor: meta type: - object - boolean security-scheme: + $comment: https://spec.openapis.org/oas/v3.1.0#security-scheme-object type: object properties: type: @@ -756,7 +799,8 @@ $defs: type: const: http scheme: - const: bearer + type: string + pattern: ^[Bb][Ee][Aa][Rr][Ee][Rr]$ required: - type - scheme @@ -764,8 +808,6 @@ $defs: properties: bearerFormat: type: string - required: - - scheme type-oauth2: if: @@ -791,12 +833,14 @@ $defs: then: properties: openIdConnectUrl: - $ref: '#/$defs/uri' + type: string + format: uri required: - openIdConnectUrl security-scheme-or-reference: if: + type: object required: - $ref then: @@ -883,6 +927,7 @@ $defs: unevaluatedProperties: false security-requirement: + $comment: https://spec.openapis.org/oas/v3.1.0#security-requirement-object type: object additionalProperties: type: array @@ -890,6 +935,7 @@ $defs: type: string specification-extensions: + $comment: https://spec.openapis.org/oas/v3.1.0#specification-extensions patternProperties: '^x-': true @@ -901,10 +947,6 @@ $defs: additionalProperties: $ref: '#/$defs/example-or-reference' - uri: - type: string - format: uri - map-of-strings: type: object additionalProperties: