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([