Add validation rule that example/examples are mutually exclusive and add examples property to Schema (3.1.0 only) (#1561)
This commit is contained in:
parent
d3265da27c
commit
2357fafbb0
|
@ -84,7 +84,9 @@
|
|||
"testlegacy": "Run tests using the legacy TokenAnalyser",
|
||||
"testall": "Run all tests (test + testlegacy)",
|
||||
"analyse": "Run static analysis (phpstan/psalm)",
|
||||
"spectral": "Run spectral lint over all .yaml files in the Examples folder",
|
||||
"spectral-examples": "Run spectral lint over all .yaml files in the Examples folder",
|
||||
"spectral-scratch": "Run spectral lint over all .yaml files in the tests/Fixtures/Scratch folder",
|
||||
"spectral": "Run all spectral tests",
|
||||
"docs:gen": "Rebuild reference documentation",
|
||||
"docs:dev": "Run dev server for local development of gh-pages",
|
||||
"docs:build": "Re-build static gh-pages"
|
||||
|
@ -105,7 +107,12 @@
|
|||
"export XDEBUG_MODE=off && phpstan analyse --memory-limit=2G",
|
||||
"export XDEBUG_MODE=off && psalm"
|
||||
],
|
||||
"spectral": "for ff in `find Examples -name '*.yaml'`; do spectral lint $ff; done",
|
||||
"spectral-examples": "for ff in `find Examples -name '*.yaml'`; do spectral lint $ff; done",
|
||||
"spectral-scratch": "for ff in `find tests/Fixtures/Scratch -name '*.yaml'`; do spectral lint $ff; done",
|
||||
"spectral": [
|
||||
"@spectral-examples",
|
||||
"@spectral-scratch"
|
||||
],
|
||||
"docs:gen": [
|
||||
"@php tools/refgen.php",
|
||||
"@php tools/procgen.php"
|
||||
|
|
|
@ -86,12 +86,12 @@ parameters:
|
|||
path: src/Processors/DocBlockDescriptions.php
|
||||
|
||||
-
|
||||
message: "#^Property OpenApi\\\\Annotations\\\\JsonContent\\:\\:\\$examples \\(array\\<string, OpenApi\\\\Annotations\\\\Examples\\>\\) does not accept string\\.$#"
|
||||
message: "#^Property OpenApi\\\\Annotations\\\\Schema\\:\\:\\$examples \\(array\\<OpenApi\\\\Annotations\\\\Examples\\>\\) does not accept string\\.$#"
|
||||
count: 1
|
||||
path: src/Processors/MergeJsonContent.php
|
||||
|
||||
-
|
||||
message: "#^Property OpenApi\\\\Annotations\\\\XmlContent\\:\\:\\$examples \\(array\\<string, OpenApi\\\\Annotations\\\\Examples\\>\\) does not accept string\\.$#"
|
||||
message: "#^Property OpenApi\\\\Annotations\\\\Schema\\:\\:\\$examples \\(array\\<OpenApi\\\\Annotations\\\\Examples\\>\\) does not accept string\\.$#"
|
||||
count: 1
|
||||
path: src/Processors/MergeXmlContent.php
|
||||
|
||||
|
|
|
@ -165,6 +165,16 @@ abstract class AbstractAnnotation implements \JsonSerializable
|
|||
$this->_context->logger->warning('Ignoring unexpected property "' . $property . '" for ' . $this->identity() . ', expecting "' . implode('", "', array_keys($fields)) . '" in ' . $this->_context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if one of the given version numbers matches the current OpenAPI version.
|
||||
*
|
||||
* @param string|array $versions One or more version numbers
|
||||
*/
|
||||
public function isOpenApiVersion($versions): bool
|
||||
{
|
||||
return $this->_context->isVersion($versions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge given annotations to their mapped properties configured in static::$_nested.
|
||||
*
|
||||
|
@ -350,7 +360,7 @@ abstract class AbstractAnnotation implements \JsonSerializable
|
|||
if (isset($data->ref)) {
|
||||
// Only specific https://github.com/OAI/OpenAPI-Specification/blob/3.1.0/versions/3.1.0.md#reference-object
|
||||
$ref = ['$ref' => $data->ref];
|
||||
if ($this->_context->version === OpenApi::VERSION_3_1_0) {
|
||||
if ($this->isOpenApiVersion(OpenApi::VERSION_3_1_0)) {
|
||||
foreach (['summary', 'description'] as $prop) {
|
||||
if (property_exists($this, $prop)) {
|
||||
if (!Generator::isDefault($this->{$prop})) {
|
||||
|
@ -361,7 +371,7 @@ abstract class AbstractAnnotation implements \JsonSerializable
|
|||
}
|
||||
if (property_exists($this, 'nullable') && $this->nullable === true) {
|
||||
$ref = ['oneOf' => [$ref]];
|
||||
if ($this->_context->version == OpenApi::VERSION_3_1_0) {
|
||||
if ($this->isOpenApiVersion(OpenApi::VERSION_3_1_0)) {
|
||||
$ref['oneOf'][] = ['type' => 'null'];
|
||||
} else {
|
||||
$ref['nullable'] = $data->nullable;
|
||||
|
@ -381,7 +391,18 @@ abstract class AbstractAnnotation implements \JsonSerializable
|
|||
$data = (object) $ref;
|
||||
}
|
||||
|
||||
if ($this->_context->version === OpenApi::VERSION_3_1_0) {
|
||||
if ($this->isOpenApiVersion(OpenApi::VERSION_3_0_0)) {
|
||||
if (isset($data->exclusiveMinimum) && is_numeric($data->exclusiveMinimum)) {
|
||||
$data->minimum = $data->exclusiveMinimum;
|
||||
$data->exclusiveMinimum = true;
|
||||
}
|
||||
if (isset($data->exclusiveMaximum) && is_numeric($data->exclusiveMaximum)) {
|
||||
$data->maximum = $data->exclusiveMaximum;
|
||||
$data->exclusiveMaximum = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isOpenApiVersion(OpenApi::VERSION_3_1_0)) {
|
||||
if (isset($data->nullable)) {
|
||||
if (true === $data->nullable) {
|
||||
if (isset($data->oneOf)) {
|
||||
|
@ -540,6 +561,13 @@ abstract class AbstractAnnotation implements \JsonSerializable
|
|||
}
|
||||
$stack[] = $this;
|
||||
|
||||
if (property_exists($this, 'example') && property_exists($this, 'examples')) {
|
||||
if (!Generator::isDefault($this->example) && !Generator::isDefault($this->examples)) {
|
||||
$valid = false;
|
||||
$this->_context->logger->warning($this->identity() . ': "example" and "examples" are mutually exclusive');
|
||||
}
|
||||
}
|
||||
|
||||
return self::_validate($this, $stack, $skip, $ref, $context) ? $valid : false;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class Components extends AbstractAnnotation
|
|||
/**
|
||||
* Reusable Examples.
|
||||
*
|
||||
* @var Examples[]
|
||||
* @var array<Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ class Examples extends AbstractAnnotation
|
|||
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
Schema::class,
|
||||
Parameter::class,
|
||||
PathParameter::class,
|
||||
MediaType::class,
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
|
||||
/**
|
||||
* A `@OA\Request` header parameter.
|
||||
*
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Annotations as OA;
|
||||
|
||||
/**
|
||||
* Shorthand for a json response.
|
||||
|
@ -17,16 +17,6 @@ use OpenApi\Generator;
|
|||
*/
|
||||
class JsonContent extends Schema
|
||||
{
|
||||
/**
|
||||
* An associative array of Examples attributes.
|
||||
*
|
||||
* The keys represent the name of the example and the values are instances of the Examples attribute.
|
||||
* Each example is used to show how the content of the request or response should look like.
|
||||
*
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
|
@ -76,7 +76,7 @@ class License extends AbstractAnnotation
|
|||
{
|
||||
$data = parent::jsonSerialize();
|
||||
|
||||
if ($this->_context->isVersion(OpenApi::VERSION_3_0_0)) {
|
||||
if ($this->isOpenApiVersion(OpenApi::VERSION_3_0_0)) {
|
||||
unset($data->identifier);
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ class License extends AbstractAnnotation
|
|||
{
|
||||
$valid = parent::validate($stack, $skip, $ref, $context);
|
||||
|
||||
if ($this->_context->isVersion(OpenApi::VERSION_3_1_0)) {
|
||||
if ($this->isOpenApiVersion(OpenApi::VERSION_3_1_0)) {
|
||||
if (!Generator::isDefault($this->url) && $this->identifier !== Generator::UNDEFINED) {
|
||||
$this->_context->logger->warning($this->identity() . ' url and identifier are mutually exclusive');
|
||||
$valid = false;
|
||||
|
|
|
@ -45,13 +45,11 @@ class MediaType extends AbstractAnnotation
|
|||
/**
|
||||
* Examples of the media type.
|
||||
*
|
||||
* Each example object should match the media type and specified schema if present.
|
||||
* Each example should contain a value in the correct format as specified in the parameter encoding.
|
||||
* The examples object is mutually exclusive of the example object.
|
||||
* Furthermore, if referencing a schema which contains an example, the examples value shall override the example provided by the schema.
|
||||
*
|
||||
* Furthermore, if referencing a schema which contains an example,
|
||||
* the examples value shall override the example provided by the schema.
|
||||
*
|
||||
* @var array<string,Examples>
|
||||
* @var array<Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -262,7 +262,7 @@ class OpenApi extends AbstractAnnotation
|
|||
{
|
||||
$data = parent::jsonSerialize();
|
||||
|
||||
if (false === $this->_context->isVersion(OpenApi::VERSION_3_1_0)) {
|
||||
if (false === $this->isOpenApiVersion(OpenApi::VERSION_3_1_0)) {
|
||||
unset($data->webhooks);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Annotations as OA;
|
||||
|
||||
/**
|
||||
* Base class for `@OA\Get`, `@OA\Post`, `@OA\Put`, etc.
|
||||
|
|
|
@ -147,13 +147,13 @@ class Parameter extends AbstractAnnotation
|
|||
public $example = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Examples of the media type.
|
||||
* Examples of the parameter.
|
||||
*
|
||||
* Each example should contain a value in the correct format as specified in the parameter encoding.
|
||||
* The examples object is mutually exclusive of the example object.
|
||||
* Furthermore, if referencing a schema which contains an example, the examples value shall override the example provided by the schema.
|
||||
*
|
||||
* @var array<string,Examples>
|
||||
* @var array<Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
|
||||
/**
|
||||
* A `@OA\Request` path parameter.
|
||||
*
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
|
||||
/**
|
||||
* A `@OA\Request` query parameter.
|
||||
*
|
||||
|
|
|
@ -57,7 +57,8 @@ class Schema extends AbstractAnnotation
|
|||
|
||||
/**
|
||||
* The maximum number of properties allowed in an object instance.
|
||||
* An object instance is valid against this property if its number of properties is less than, or equal to, the value of this attribute.
|
||||
* An object instance is valid against this property if its number of properties is less than, or equal to, the
|
||||
* value of this attribute.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
|
@ -65,7 +66,8 @@ class Schema extends AbstractAnnotation
|
|||
|
||||
/**
|
||||
* The minimum number of properties allowed in an object instance.
|
||||
* An object instance is valid against this property if its number of properties is greater than, or equal to, the value of this attribute.
|
||||
* An object instance is valid against this property if its number of properties is greater than, or equal to, the
|
||||
* value of this attribute.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
|
@ -121,9 +123,9 @@ class Schema extends AbstractAnnotation
|
|||
* - ssv: space separated values foo bar.
|
||||
* - tsv: tab separated values foo\tbar.
|
||||
* - pipes: pipe separated values foo|bar.
|
||||
* - multi: corresponds to multiple parameter instances instead of multiple values for a single instance foo=bar&foo=baz.
|
||||
* This is valid only for parameters of type <code>query</code> or <code>formData</code>.
|
||||
* Default value is csv.
|
||||
* - multi: corresponds to multiple parameter instances instead of multiple values for a single instance
|
||||
* foo=bar&foo=baz. This is valid only for parameters of type <code>query</code> or <code>formData</code>. Default
|
||||
* value is csv.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
@ -179,7 +181,8 @@ class Schema extends AbstractAnnotation
|
|||
/**
|
||||
* The maximum length of a string property.
|
||||
*
|
||||
* A string instance is valid against this property if its length is less than, or equal to, the value of this attribute.
|
||||
* A string instance is valid against this property if its length is less than, or equal to, the value of this
|
||||
* attribute.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor26)
|
||||
*
|
||||
|
@ -190,7 +193,8 @@ class Schema extends AbstractAnnotation
|
|||
/**
|
||||
* The minimum length of a string property.
|
||||
*
|
||||
* A string instance is valid against this property if its length is greater than, or equal to, the value of this attribute.
|
||||
* A string instance is valid against this property if its length is greater than, or equal to, the value of this
|
||||
* attribute.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor29)
|
||||
*
|
||||
|
@ -208,7 +212,8 @@ class Schema extends AbstractAnnotation
|
|||
/**
|
||||
* The maximum number of items allowed in an array property.
|
||||
*
|
||||
* An array instance is valid against this property if its number of items is less than, or equal to, the value of this attribute.
|
||||
* An array instance is valid against this property if its number of items is less than, or equal to, the value of
|
||||
* this attribute.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor42)
|
||||
*
|
||||
|
@ -219,7 +224,8 @@ class Schema extends AbstractAnnotation
|
|||
/**
|
||||
* The minimum number of items allowed in an array property.
|
||||
*
|
||||
* An array instance is valid against this property if its number of items is greater than, or equal to, the value of this attribute.
|
||||
* An array instance is valid against this property if its number of items is greater than, or equal to, the value
|
||||
* of this attribute.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor45)
|
||||
*
|
||||
|
@ -241,7 +247,8 @@ class Schema extends AbstractAnnotation
|
|||
/**
|
||||
* A collection of allowable values for a property.
|
||||
*
|
||||
* A property instance is valid against this attribute if its value is one of the values specified in this collection.
|
||||
* A property instance is valid against this attribute if its value is one of the values specified in this
|
||||
* collection.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor76)
|
||||
*
|
||||
|
@ -319,6 +326,19 @@ class Schema extends AbstractAnnotation
|
|||
*/
|
||||
public $example = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Examples of the schema.
|
||||
*
|
||||
* Each example should contain a value in the correct format as specified in the parameter encoding.
|
||||
* The examples object is mutually exclusive of the example object.
|
||||
* Furthermore, if referencing a schema which contains an example, the examples value shall override the example provided by the schema.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @var array<Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Allows sending a null value for the defined schema.
|
||||
* Default value is false.
|
||||
|
@ -439,6 +459,7 @@ class Schema extends AbstractAnnotation
|
|||
Items::class => 'items',
|
||||
Property::class => ['properties', 'property'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Examples::class => ['examples', 'example'],
|
||||
Xml::class => 'xml',
|
||||
AdditionalProperties::class => 'additionalProperties',
|
||||
Attachable::class => ['attachables'],
|
||||
|
@ -463,8 +484,9 @@ class Schema extends AbstractAnnotation
|
|||
{
|
||||
$data = parent::jsonSerialize();
|
||||
|
||||
if (isset($data->const)) {
|
||||
if ($this->_context->isVersion(OpenApi::VERSION_3_0_0)) {
|
||||
if ($this->isOpenApiVersion(OpenApi::VERSION_3_0_0)) {
|
||||
unset($data->examples);
|
||||
if (isset($data->const)) {
|
||||
$data->enum = [$data->const];
|
||||
unset($data->const);
|
||||
}
|
||||
|
@ -484,6 +506,14 @@ class Schema extends AbstractAnnotation
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($this->isOpenApiVersion(OpenApi::VERSION_3_0_0)) {
|
||||
if (!Generator::isDefault($this->examples)) {
|
||||
$this->_context->logger->warning($this->identity() . ' is only allowed for ' . OpenApi::VERSION_3_1_0);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::validate($stack, $skip, $ref, $context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Annotations as OA;
|
||||
|
||||
/**
|
||||
* Shorthand for a xml response.
|
||||
|
@ -17,11 +17,6 @@ use OpenApi\Generator;
|
|||
*/
|
||||
class XmlContent extends Schema
|
||||
{
|
||||
/**
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
|
@ -16,7 +16,7 @@ class Components extends \OpenApi\Annotations\Components
|
|||
* @param Response[]|null $responses
|
||||
* @param Parameter[]|null $parameters
|
||||
* @param RequestBody[]|null $requestBodies
|
||||
* @param Examples[]|null $examples
|
||||
* @param array<Examples>|null $examples
|
||||
* @param Header[]|null $headers
|
||||
* @param SecurityScheme[]|null $securitySchemes
|
||||
* @param Link[]|null $links
|
||||
|
|
|
@ -14,7 +14,7 @@ class JsonContent extends \OpenApi\Annotations\JsonContent
|
|||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,Examples> $examples
|
||||
* @param array<Examples> $examples
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
|
|
|
@ -12,7 +12,7 @@ use OpenApi\Generator;
|
|||
class MediaType extends \OpenApi\Annotations\MediaType
|
||||
{
|
||||
/**
|
||||
* @param array<string,Examples> $examples
|
||||
* @param array<Examples> $examples
|
||||
* @param array<string,mixed> $encoding
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
|
|
|
@ -12,7 +12,7 @@ trait ParameterTrait
|
|||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,Examples> $examples
|
||||
* @param array<Examples> $examples
|
||||
* @param array<MediaType>|JsonContent|XmlContent|Attachable|null $content
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Annotations\Examples;
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||
|
@ -19,6 +20,7 @@ class Schema extends \OpenApi\Annotations\Schema
|
|||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param array<string|int|float|bool|\UnitEnum|null>|class-string|null $enum
|
||||
* @param array<Examples> $examples
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $allOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $anyOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $oneOf
|
||||
|
@ -57,6 +59,7 @@ class Schema extends \OpenApi\Annotations\Schema
|
|||
?Xml $xml = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
mixed $example = Generator::UNDEFINED,
|
||||
?array $examples = null,
|
||||
?bool $nullable = null,
|
||||
?bool $deprecated = null,
|
||||
?array $allOf = null,
|
||||
|
@ -105,7 +108,7 @@ class Schema extends \OpenApi\Annotations\Schema
|
|||
'const' => $const,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'attachables' => $attachables ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($items, $discriminator, $externalDocs, $attachables),
|
||||
'value' => $this->combine($items, $discriminator, $externalDocs, $examples, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class XmlContent extends \OpenApi\Annotations\XmlContent
|
|||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,Examples> $examples
|
||||
* @param array<Examples> $examples
|
||||
* @param string[] $required
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
|
|
|
@ -144,7 +144,11 @@ class AugmentProperties implements ProcessorInterface
|
|||
$property->minimum = 0;
|
||||
} elseif ($type === 'non-zero-int') {
|
||||
$property->type = 'integer';
|
||||
$property->not = ['const' => 0];
|
||||
if ($property->isOpenApiVersion(OA\OpenApi::VERSION_3_1_0)) {
|
||||
$property->not = ['const' => 0];
|
||||
} else {
|
||||
$property->not = ['enum' => [0]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ use OpenApi\Analysers\AnnotationFactoryInterface;
|
|||
use OpenApi\Analysers\AttributeAnnotationFactory;
|
||||
use OpenApi\Analysers\DocBlockAnnotationFactory;
|
||||
use OpenApi\Analysers\ReflectionAnalyser;
|
||||
use OpenApi\Analysers\TokenAnalyser;
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Context;
|
||||
|
@ -184,9 +183,7 @@ class ReflectionAnalyserTest extends OpenApiTestCase
|
|||
*/
|
||||
public function testPhp8PromotedProperties(): void
|
||||
{
|
||||
if ($this->getAnalyzer() instanceof TokenAnalyser) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
$this->skipLegacy();
|
||||
|
||||
$analysis = $this->analysisFromFixtures(['PHP/Php8PromotedProperties.php']);
|
||||
$schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true);
|
||||
|
|
|
@ -142,6 +142,23 @@ END;
|
|||
$this->assertFalse((new OA\AdditionalProperties([]))->isRoot(OA\Schema::class));
|
||||
$this->assertTrue((new SubSchema([]))->isRoot(OA\Schema::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8.1
|
||||
*/
|
||||
public function testValidateExamples(): void
|
||||
{
|
||||
$this->skipLegacy();
|
||||
|
||||
$analysis = $this->analysisFromFixtures(['BadExampleParameter.php']);
|
||||
$analysis->process((new Generator())->getProcessors());
|
||||
|
||||
$this->assertOpenApiLogEntryContains('Required @OA\PathItem() not found');
|
||||
$this->assertOpenApiLogEntryContains('Required @OA\Info() not found');
|
||||
$this->assertOpenApiLogEntryContains('"example" and "examples" are mutually exclusive');
|
||||
|
||||
$analysis->validate();
|
||||
}
|
||||
}
|
||||
|
||||
class SubSchema extends OA\Schema
|
||||
|
|
|
@ -64,13 +64,19 @@ class AttributesSyncTest extends OpenApiTestCase
|
|||
}
|
||||
if (!$found) {
|
||||
// exclusions...
|
||||
if ($attributeRC->isSubclassOf(OA\Operation::class) && 'method' == $propertyName) {
|
||||
if ($attributeRC->isSubclassOf(OA\Operation::class) && in_array($propertyName, ['method'])) {
|
||||
continue;
|
||||
}
|
||||
if ($attributeRC->isSubclassOf(OA\Attachable::class) && 'x' == $propertyName) {
|
||||
if ($attributeRC->isSubclassOf(OA\Attachable::class) && in_array($propertyName, ['x'])) {
|
||||
continue;
|
||||
}
|
||||
if ($attributeRC->isSubclassOf(OA\AdditionalProperties::class) && 'additionalProperties' == $propertyName) {
|
||||
if ($attributeRC->isSubclassOf(OA\AdditionalProperties::class) && in_array($propertyName, ['additionalProperties', 'examples'])) {
|
||||
continue;
|
||||
}
|
||||
if ($attributeRC->isSubclassOf(OA\Items::class) && in_array($propertyName, ['examples'])) {
|
||||
continue;
|
||||
}
|
||||
if ($attributeRC->isSubclassOf(OA\Property::class) && in_array($propertyName, ['examples'])) {
|
||||
continue;
|
||||
}
|
||||
if (in_array($propertyName, static::$SCHEMA_EXCLUSIONS)) {
|
||||
|
|
|
@ -17,7 +17,7 @@ class OpenApiTest extends OpenApiTestCase
|
|||
$this->assertOpenApiLogEntryContains('Required @OA\Info() not found');
|
||||
|
||||
$openapi = new OA\OpenApi(['_context' => $this->getContext()]);
|
||||
$openapi->openapi = '3.0.0';
|
||||
$openapi->openapi = OA\OpenApi::VERSION_3_0_0;
|
||||
$openapi->validate();
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ class OpenApiTest extends OpenApiTestCase
|
|||
$this->assertOpenApiLogEntryContains("At least one of 'Required @OA\PathItem(), @OA\Components() or @OA\Webhook() not found'");
|
||||
|
||||
$openapi = new OA\OpenApi(['_context' => $this->getContext()]);
|
||||
$openapi->openapi = '3.1.0';
|
||||
$openapi->openapi = OA\OpenApi::VERSION_3_1_0;
|
||||
$openapi->validate();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use OpenApi\Tests\Fixtures\Attributes as OAF;
|
|||
/**
|
||||
* The Spec.
|
||||
*/
|
||||
#[OAT\OpenApi(openapi: '3.1.0', security: [['bearerAuth' => []]])]
|
||||
#[OAT\OpenApi(openapi: OAT\OpenApi::VERSION_3_1_0, security: [['bearerAuth' => []]])]
|
||||
#[OAT\Info(
|
||||
version: '1.0.0',
|
||||
title: 'Basic single file API',
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Tests\Fixtures;
|
||||
|
||||
use OpenApi\Attributes as OAT;
|
||||
|
||||
#[OAT\QueryParameter(
|
||||
name: 'bad',
|
||||
example: 'not good',
|
||||
examples: [new OAT\Examples(example: 'first', summary: 'First example', value: false)]
|
||||
)]
|
||||
class BadExampleParameter
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Attribute Inheritance Scratch'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: 6e8c7c1a5488a11e2b1bf5f3b726c29c
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
Base:
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
type: object
|
||||
Child1:
|
||||
type: object
|
||||
allOf:
|
||||
-
|
||||
$ref: '#/components/schemas/Base'
|
||||
-
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
Child2:
|
||||
type: object
|
||||
allOf:
|
||||
-
|
||||
$ref: '#/components/schemas/Base'
|
||||
-
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
type: object
|
|
@ -0,0 +1,18 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: ClassRef
|
||||
version: '1.0'
|
||||
paths:
|
||||
/endpoint:
|
||||
get:
|
||||
operationId: f835f4c3d03452d2e5d2f48b7c314e2f
|
||||
responses:
|
||||
'200':
|
||||
description: 'All good'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/YoYo'
|
||||
components:
|
||||
schemas:
|
||||
YoYo: { }
|
|
@ -0,0 +1,63 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/target_groups:
|
||||
get:
|
||||
tags:
|
||||
- 'Target groups'
|
||||
summary: 'List target groups'
|
||||
operationId: groups
|
||||
responses:
|
||||
'200':
|
||||
description: 'Successful response of [TargetGroupListDto]'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TargetGroupListDto'
|
||||
components:
|
||||
schemas:
|
||||
TargetGroupListDto:
|
||||
title: TargetGroupListDto
|
||||
required:
|
||||
- targetGroups
|
||||
properties:
|
||||
targetGroups:
|
||||
title: TargetGroupDto
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TargetGroupDto'
|
||||
type: object
|
||||
TargetGroupDto:
|
||||
title: TargetGroupDto
|
||||
required:
|
||||
- groupId
|
||||
- groupName
|
||||
- targets
|
||||
properties:
|
||||
groupId:
|
||||
type: string
|
||||
groupName:
|
||||
type: string
|
||||
targets:
|
||||
title: TargetDto
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TargetDto'
|
||||
type: object
|
||||
TargetDto:
|
||||
title: TargetDto
|
||||
required:
|
||||
- targetId
|
||||
- targetType
|
||||
properties:
|
||||
targetId:
|
||||
$ref: '#/components/schemas/TargetId'
|
||||
targetType:
|
||||
$ref: '#/components/schemas/TargetType'
|
||||
type: object
|
||||
TargetId:
|
||||
title: TargetId
|
||||
TargetType:
|
||||
title: TargetType
|
|
@ -0,0 +1,15 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Custom Attribute Schema Scratch'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: da8efe0fc0b09040d0bda7cf58d9421d
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
MyClass: { }
|
|
@ -0,0 +1,29 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Extended Attributes Scratch'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: 242c3431636158b96b85e2adbefa76f4
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
CAItemModel: { }
|
||||
CAModel:
|
||||
properties:
|
||||
name:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
item:
|
||||
$ref: '#/components/schemas/CAItemModel'
|
||||
items:
|
||||
title: CAItemModel
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/CAItemModel'
|
||||
type: object
|
|
@ -0,0 +1,20 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: Api
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/api:
|
||||
get:
|
||||
operationId: 232f976e7ad23d1e147e984eb3da3506
|
||||
responses:
|
||||
'200':
|
||||
description: 'All good'
|
||||
components:
|
||||
schemas:
|
||||
CustomPropertyAttribute:
|
||||
properties:
|
||||
number:
|
||||
type:
|
||||
- integer
|
||||
- 'null'
|
||||
type: object
|
|
@ -9,8 +9,14 @@ namespace OpenApi\Tests\Fixtures\Scratch;
|
|||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Attributes as OAT;
|
||||
|
||||
/** @OA\Schema */
|
||||
class DocblockSchema
|
||||
/**
|
||||
* @OA\OpenApi(
|
||||
* openapi="3.0.0"
|
||||
* )
|
||||
*
|
||||
* @OA\Schema
|
||||
*/
|
||||
class DocblocksSchema
|
||||
{
|
||||
/**
|
||||
* @OA\Property
|
||||
|
@ -76,7 +82,7 @@ class DocblockSchema
|
|||
}
|
||||
|
||||
#[OAT\Schema]
|
||||
class DocblockSchemaChild extends DocblockSchema
|
||||
class DocblockSchemaChild extends DocblocksSchema
|
||||
{
|
||||
/** @var int The id */
|
||||
#[OAT\Property]
|
||||
|
@ -84,7 +90,7 @@ class DocblockSchemaChild extends DocblockSchema
|
|||
}
|
||||
|
||||
/**
|
||||
* @OA\Info(title="API", version="1.0")
|
||||
* @OA\Info(title="Dockblocks", version="1.0")
|
||||
* @OA\Get(
|
||||
* path="/api/endpoint",
|
||||
* @OA\Response(
|
||||
|
@ -93,6 +99,6 @@ class DocblockSchemaChild extends DocblockSchema
|
|||
* )
|
||||
* )
|
||||
*/
|
||||
class DockblockEndpoint
|
||||
class DockblocksEndpoint
|
||||
{
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: Dockblocks
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
operationId: 2dd3513e31e559b14abab58814959d68
|
||||
responses:
|
||||
'200':
|
||||
description: 'successful operation'
|
||||
components:
|
||||
schemas:
|
||||
DocblocksSchema:
|
||||
properties:
|
||||
name:
|
||||
description: 'The name'
|
||||
type: string
|
||||
oldName:
|
||||
description: 'The name (old)'
|
||||
type: string
|
||||
deprecated: true
|
||||
rangeInt:
|
||||
description: 'The range integer'
|
||||
type: integer
|
||||
maximum: 25
|
||||
minimum: 5
|
||||
minRangeInt:
|
||||
description: 'The minimum range integer'
|
||||
type: integer
|
||||
minimum: 2
|
||||
maxRangeInt:
|
||||
description: 'The maximum range integer'
|
||||
type: integer
|
||||
maximum: 10
|
||||
positiveInt:
|
||||
description: 'The positive integer'
|
||||
type: integer
|
||||
minimum: 1
|
||||
negativeInt:
|
||||
description: 'The negative integer'
|
||||
type: integer
|
||||
maximum: -1
|
||||
nonPositiveInt:
|
||||
description: 'The non-positive integer'
|
||||
type: integer
|
||||
maximum: 0
|
||||
nonNegativeInt:
|
||||
description: 'The non-negative integer'
|
||||
type: integer
|
||||
minimum: 0
|
||||
nonZeroInt:
|
||||
description: 'The non-zero integer'
|
||||
type: integer
|
||||
not:
|
||||
enum:
|
||||
- 0
|
||||
type: object
|
||||
DocblockSchemaChild:
|
||||
type: object
|
||||
allOf:
|
||||
-
|
||||
$ref: '#/components/schemas/DocblocksSchema'
|
||||
-
|
||||
properties:
|
||||
id:
|
||||
description: 'The id'
|
||||
type: integer
|
||||
type: object
|
|
@ -1,17 +1,17 @@
|
|||
openapi: 3.0.0
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: API
|
||||
title: Dockblocks
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
operationId: deb4d2aa2d9e471e6e6a6c8ed61123d0
|
||||
operationId: 2dd3513e31e559b14abab58814959d68
|
||||
responses:
|
||||
'200':
|
||||
description: 'successful operation'
|
||||
components:
|
||||
schemas:
|
||||
DocblockSchema:
|
||||
DocblocksSchema:
|
||||
properties:
|
||||
name:
|
||||
description: 'The name'
|
||||
|
@ -23,8 +23,8 @@ components:
|
|||
rangeInt:
|
||||
description: 'The range integer'
|
||||
type: integer
|
||||
minimum: 5
|
||||
maximum: 25
|
||||
minimum: 5
|
||||
minRangeInt:
|
||||
description: 'The minimum range integer'
|
||||
type: integer
|
||||
|
@ -54,13 +54,12 @@ components:
|
|||
type: integer
|
||||
not:
|
||||
const: 0
|
||||
|
||||
type: object
|
||||
DocblockSchemaChild:
|
||||
type: object
|
||||
allOf:
|
||||
-
|
||||
$ref: '#/components/schemas/DocblockSchema'
|
||||
$ref: '#/components/schemas/DocblocksSchema'
|
||||
-
|
||||
properties:
|
||||
id:
|
|
@ -0,0 +1,29 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: DuplicateRef
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: 3f9d6af187f46eeacb56ecc9ebbf5fba
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
abstract-user:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
type: object
|
||||
create-user:
|
||||
allOf:
|
||||
-
|
||||
$ref: '#/components/schemas/abstract-user'
|
||||
-
|
||||
required:
|
||||
- name
|
||||
- email
|
|
@ -0,0 +1,59 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Tests\Fixtures\Scratch;
|
||||
|
||||
use OpenApi\Attributes as OAT;
|
||||
|
||||
#[OAT\Schema(
|
||||
schema: 'YoYo',
|
||||
examples: [
|
||||
new OAT\Examples(
|
||||
example: 'yo',
|
||||
summary: 'the yo',
|
||||
value: 'YoYo'
|
||||
),
|
||||
]
|
||||
)]
|
||||
class ExampleSchema
|
||||
{
|
||||
}
|
||||
|
||||
#[OAT\Info(title: 'Examples', version: '1.0')]
|
||||
#[OAT\Get(
|
||||
path: '/endpoint/{name}/{other}',
|
||||
parameters: [
|
||||
new OAT\PathParameter(
|
||||
name: 'name',
|
||||
required: true,
|
||||
schema: new OAT\Schema(type: 'string'),
|
||||
example: 'Fritz'
|
||||
),
|
||||
new OAT\PathParameter(
|
||||
name: 'other',
|
||||
required: true,
|
||||
schema: new OAT\Schema(type: 'string'),
|
||||
examples: [
|
||||
new OAT\Examples(
|
||||
example: 'o1',
|
||||
summary: 'other example 1',
|
||||
value: 'ping'
|
||||
),
|
||||
new OAT\Examples(
|
||||
example: 'o2',
|
||||
summary: 'other example 2',
|
||||
value: 'pong'
|
||||
),
|
||||
]
|
||||
),
|
||||
],
|
||||
responses: [
|
||||
new OAT\Response(response: 200, description: 'OK'),
|
||||
]
|
||||
)]
|
||||
class ExamplesEndpoint
|
||||
{
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: Examples
|
||||
version: '1.0'
|
||||
paths:
|
||||
'/endpoint/{name}/{other}':
|
||||
get:
|
||||
operationId: fc70869eba6848b340d6f3546b63e344
|
||||
parameters:
|
||||
-
|
||||
name: name
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: Fritz
|
||||
-
|
||||
name: other
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
examples:
|
||||
o1:
|
||||
summary: 'other example 1'
|
||||
value: ping
|
||||
o2:
|
||||
summary: 'other example 2'
|
||||
value: pong
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
YoYo: { }
|
|
@ -0,0 +1,39 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: Examples
|
||||
version: '1.0'
|
||||
paths:
|
||||
'/endpoint/{name}/{other}':
|
||||
get:
|
||||
operationId: fc70869eba6848b340d6f3546b63e344
|
||||
parameters:
|
||||
-
|
||||
name: name
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: Fritz
|
||||
-
|
||||
name: other
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
examples:
|
||||
o1:
|
||||
summary: 'other example 1'
|
||||
value: ping
|
||||
o2:
|
||||
summary: 'other example 2'
|
||||
value: pong
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
YoYo:
|
||||
examples:
|
||||
yo:
|
||||
summary: 'the yo'
|
||||
value: YoYo
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
use OpenApi\Attributes as OAT;
|
||||
|
||||
#[OAT\Schema(schema: 'minMaxClass31')]
|
||||
class MinMaxClass31
|
||||
#[OAT\Schema(schema: 'minMaxClass')]
|
||||
class MinMaxClass
|
||||
{
|
||||
#[OAT\Property(minimum: 10)]
|
||||
private int $min = 10;
|
||||
|
@ -25,12 +25,11 @@ class MinMaxClass31
|
|||
private int $exclusiveMinMaxNumber = 61;
|
||||
}
|
||||
|
||||
#[OAT\OpenApi(openapi: '3.1.0')]
|
||||
#[OAT\Info(
|
||||
title: 'Exclusive minimum and maximum',
|
||||
version: '1.0'
|
||||
)]
|
||||
class ExclusiveMinMax31
|
||||
class ExclusiveMinMax
|
||||
{
|
||||
#[OAT\Get(
|
||||
path: '/api/endpoint',
|
|
@ -0,0 +1,43 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: 'Exclusive minimum and maximum'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: ef57acec977120506db6b2cf1c500c15
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
minMaxClass:
|
||||
properties:
|
||||
min:
|
||||
type: integer
|
||||
minimum: 10
|
||||
exclusiveMin:
|
||||
type: integer
|
||||
minimum: 20
|
||||
exclusiveMinimum: true
|
||||
max:
|
||||
type: integer
|
||||
maximum: 30
|
||||
exclusiveMax:
|
||||
type: integer
|
||||
maximum: 40
|
||||
exclusiveMaximum: true
|
||||
exclusiveMinMax:
|
||||
type: integer
|
||||
maximum: 60
|
||||
exclusiveMaximum: true
|
||||
minimum: 50
|
||||
exclusiveMinimum: true
|
||||
exclusiveMinMaxNumber:
|
||||
type: integer
|
||||
exclusiveMaximum: true
|
||||
exclusiveMinimum: true
|
||||
minimum: 60
|
||||
maximum: 70
|
||||
type: object
|
|
@ -6,13 +6,13 @@ paths:
|
|||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: da2bbb06428a6f5ae9199a22a80436d7
|
||||
operationId: ef57acec977120506db6b2cf1c500c15
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
minMaxClass31:
|
||||
minMaxClass:
|
||||
properties:
|
||||
min:
|
||||
type: integer
|
|
@ -0,0 +1,38 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: API
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
operationId: f2c3bf0cddeaf2c12b195131fb1d4e5f
|
||||
responses:
|
||||
'200':
|
||||
description: 'successful operation'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Address'
|
||||
components:
|
||||
schemas:
|
||||
Address:
|
||||
required:
|
||||
- street
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
readOnly: true
|
||||
street:
|
||||
type: string
|
||||
type: object
|
||||
xml:
|
||||
name: Address
|
|
@ -0,0 +1,55 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: API
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
operationId: 7bc218b8e28854bc705f3b582ab21668
|
||||
responses:
|
||||
'200':
|
||||
description: 'successful operation'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Product'
|
||||
components:
|
||||
schemas:
|
||||
ModelExtended:
|
||||
description: 'This model can be ignored, it is just used for inheritance.'
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
readOnly: true
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
type: object
|
||||
Product:
|
||||
description: Product
|
||||
required:
|
||||
- number
|
||||
- name
|
||||
type: object
|
||||
xml:
|
||||
name: Product
|
||||
allOf:
|
||||
-
|
||||
$ref: '#/components/schemas/ModelExtended'
|
||||
-
|
||||
properties:
|
||||
deleted_at:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
number:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
type: object
|
|
@ -0,0 +1,33 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Multiple Paths For Endpoint Scratch'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/class/endpoint:
|
||||
get:
|
||||
description: 'A class endpoint'
|
||||
operationId: f6fa254544c561ced460509c98adbc8b
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
/api/class/endpoint2:
|
||||
get:
|
||||
description: 'Another class endpoint'
|
||||
operationId: 7816e3a6652e1e4782355ec5675e75f8
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
/api/method/endpoint:
|
||||
get:
|
||||
description: 'A method endpoint'
|
||||
operationId: b134e3908c5787b578f7faaa63283168
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
/api/method/endpoint2:
|
||||
get:
|
||||
description: 'Another method endpoint'
|
||||
operationId: ff3758f75b33d1e680c47e92ed39844f
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
|
@ -0,0 +1,19 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Nested Additional Properties'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: b81d5bbc60cb1ce6c15eb67081315573
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
NestedAdditionalAttributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
additionalProperties:
|
||||
type: string
|
|
@ -0,0 +1,30 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Parameter Content Scratch'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
operationId: b870e13084c8a89270151bb3e43f215a
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
NestedSchema:
|
||||
required:
|
||||
- errors
|
||||
properties:
|
||||
errors:
|
||||
description: 'Validation errors'
|
||||
type: object
|
||||
minItems: 1
|
||||
uniqueItems: true
|
||||
additionalProperties:
|
||||
description: 'Array of error messages for property'
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
uniqueItems: true
|
||||
type: object
|
|
@ -1,57 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
use OpenApi\Attributes as OAT;
|
||||
|
||||
#[OAT\Schema(schema: 'repository')]
|
||||
class Repository31
|
||||
{
|
||||
}
|
||||
|
||||
#[OAT\OpenApi(openapi: '3.1.0')]
|
||||
#[OAT\Info(
|
||||
title: 'Null Ref',
|
||||
version: '1.0'
|
||||
)]
|
||||
class NullRef31
|
||||
{
|
||||
#[OAT\Get(
|
||||
path: '/api/refonly',
|
||||
operationId: 'refonly',
|
||||
responses: [
|
||||
new OAT\Response(
|
||||
response: 200,
|
||||
description: 'Ref response',
|
||||
content: new OAT\JsonContent(
|
||||
ref: '#/components/schemas/repository',
|
||||
nullable: true
|
||||
)
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function refonly()
|
||||
{
|
||||
}
|
||||
|
||||
#[OAT\Get(
|
||||
path: '/api/refplus',
|
||||
operationId: 'refplus',
|
||||
responses: [
|
||||
new OAT\Response(
|
||||
response: 200,
|
||||
description: 'Ref plus response',
|
||||
content: new OAT\JsonContent(
|
||||
ref: '#/components/schemas/repository',
|
||||
description: 'The repository',
|
||||
nullable: true
|
||||
)
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function refplusy()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ namespace OpenApi\Tests\Fixtures\Scratch;
|
|||
|
||||
use OpenApi\Attributes as OAT;
|
||||
|
||||
#[OAT\OpenApi(openapi: '3.1.0')]
|
||||
#[OAT\Info(title: 'Nullable', version: '1.0')]
|
||||
class Api
|
||||
{
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: Nullable
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: 4608e00dd49333806891310fdc161547
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
MyDateTime:
|
||||
type: string
|
||||
format: rfc3339-timestamp
|
||||
externalDocs:
|
||||
description: '**RFC3339** IETF'
|
||||
url: 'https://tools.ietf.org/html/rfc3339'
|
||||
example: '2023-08-02T07:06:46+03:30'
|
||||
Nullable:
|
||||
properties:
|
||||
firstname:
|
||||
type: string
|
||||
nullable: true
|
||||
lastname:
|
||||
type: string
|
||||
nullable: true
|
||||
birthdate:
|
||||
nullable: true
|
||||
oneOf:
|
||||
-
|
||||
$ref: '#/components/schemas/MyDateTime'
|
||||
otherdate:
|
||||
nullable: true
|
||||
oneOf:
|
||||
-
|
||||
$ref: '#/components/schemas/MyDateTime'
|
||||
anotherdate:
|
||||
nullable: true
|
||||
oneOf:
|
||||
-
|
||||
$ref: '#/components/schemas/MyDateTime'
|
||||
description:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
choice:
|
||||
type: string
|
||||
enum:
|
||||
- Choice1
|
||||
- Choice2
|
||||
- null
|
||||
example: Choice1
|
||||
nullable: true
|
||||
type: object
|
|
@ -0,0 +1,26 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Parameter Content Scratch'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
tags:
|
||||
- endpoints
|
||||
summary: 'An API endpoint.'
|
||||
description: 'An endpoint'
|
||||
operationId: endpoint
|
||||
parameters:
|
||||
-
|
||||
name: filter
|
||||
in: query
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
type: { type: string }
|
||||
color: { type: string }
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
|
@ -0,0 +1,26 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Promoted Property Description Scratch'
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: ff3eaf9da24371c37532fd0de82a3212
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
PromotedPropertyDescription:
|
||||
properties:
|
||||
thevalue:
|
||||
description: 'Property value.'
|
||||
type: string
|
||||
thename:
|
||||
description: 'Property name.'
|
||||
type: string
|
||||
themeta:
|
||||
description: 'Property meta.'
|
||||
type: string
|
||||
type: object
|
|
@ -0,0 +1,22 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: API
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
operationId: 6e9f788346a2235f83520975c9ba68c8
|
||||
responses:
|
||||
'200':
|
||||
description: 'successful operation'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PropertyInheritance'
|
||||
components:
|
||||
schemas:
|
||||
PropertyInheritance:
|
||||
properties:
|
||||
inheritedfilter:
|
||||
type: string
|
||||
type: object
|
|
@ -0,0 +1,35 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: RequestBody
|
||||
version: '1.0'
|
||||
paths:
|
||||
/endpoint:
|
||||
get:
|
||||
operationId: 30597cf43b480393042f6b01a9d7980c
|
||||
requestBody:
|
||||
description: 'Information about a new pet in the system'
|
||||
content:
|
||||
application/json: { }
|
||||
responses:
|
||||
'200':
|
||||
description: 'All good'
|
||||
/endpoint/ref:
|
||||
post:
|
||||
operationId: 4250dd87e4e3872a8f2e481532cbc245
|
||||
requestBody:
|
||||
$ref: '#/components/requestBodies/RequestBodyRef'
|
||||
responses:
|
||||
'200':
|
||||
description: 'All good'
|
||||
/endpoint/ref-foo:
|
||||
post:
|
||||
operationId: 344406e28927343e4e9e4f39bd6c385b
|
||||
requestBody:
|
||||
$ref: '#/components/requestBodies/foo'
|
||||
responses:
|
||||
'200':
|
||||
description: 'All good'
|
||||
components:
|
||||
requestBodies:
|
||||
RequestBodyRef: { }
|
||||
foo: { }
|
|
@ -0,0 +1,18 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: API
|
||||
version: '1.0'
|
||||
paths:
|
||||
/api/endpoint:
|
||||
get:
|
||||
operationId: d5f395c872bfd6677e6c30c55c7d5463
|
||||
responses:
|
||||
'200':
|
||||
description: 'successful operation'
|
||||
components:
|
||||
schemas:
|
||||
SomeParent: { }
|
||||
Child:
|
||||
allOf:
|
||||
-
|
||||
$ref: '#/components/schemas/SomeParent'
|
|
@ -9,7 +9,7 @@ namespace OpenApi\Tests\Fixtures\Scratch;
|
|||
use OpenApi\Attributes as OAT;
|
||||
|
||||
#[OAT\Schema]
|
||||
class Types31
|
||||
class Types
|
||||
{
|
||||
#[OAT\Property(type: ['string', 'integer'])]
|
||||
public string|int $stringInteger = '';
|
||||
|
@ -18,7 +18,7 @@ class Types31
|
|||
public mixed $massiveTypes = '';
|
||||
}
|
||||
|
||||
#[OAT\OpenApi(openapi: '3.1.0')]
|
||||
#[OAT\OpenApi(openapi: OAT\OpenApi::VERSION_3_1_0)]
|
||||
#[OAT\Info(
|
||||
title: 'List of types',
|
||||
version: '1.0'
|
||||
|
@ -28,6 +28,6 @@ class Types31
|
|||
description: 'An endpoint',
|
||||
responses: [new OAT\Response(response: 200, description: 'OK')]
|
||||
)]
|
||||
class Types31Endpoint
|
||||
class TypesEndpoint
|
||||
{
|
||||
}
|
|
@ -6,13 +6,13 @@ paths:
|
|||
/api/endpoint:
|
||||
get:
|
||||
description: 'An endpoint'
|
||||
operationId: 20a99cdde4e09b297c7555321c7cdb6f
|
||||
operationId: 04621eecf5d57d633b0be3e42a30a379
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
Types31:
|
||||
Types:
|
||||
properties:
|
||||
stringInteger:
|
||||
type:
|
|
@ -8,7 +8,7 @@ namespace OpenApi\Tests\Fixtures\Scratch;
|
|||
|
||||
use OpenApi\Attributes as OAT;
|
||||
|
||||
#[OAT\PathParameter(name: 'itemName', description: 'The item name')]
|
||||
#[OAT\PathParameter(name: 'item_name', description: 'The item name', required: true, schema: new OAT\Schema(type: 'string'))]
|
||||
class UsingRefsParameter
|
||||
{
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class UsingRefsResponse
|
|||
#[OAT\Get(
|
||||
path: '/item/{item_name}',
|
||||
parameters: [
|
||||
new OAT\Parameter(ref: '#/components/parameters/itemName'),
|
||||
new OAT\Parameter(ref: '#/components/parameters/item_name'),
|
||||
],
|
||||
responses: [
|
||||
new OAT\Response(response: 200, ref: '#/components/responses/item'),
|
||||
|
|
|
@ -8,7 +8,7 @@ paths:
|
|||
operationId: 6ecb3788642c6ba8ce8d99cbcd554dbe
|
||||
parameters:
|
||||
-
|
||||
$ref: '#/components/parameters/itemName'
|
||||
$ref: '#/components/parameters/item_name'
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/components/responses/item'
|
||||
|
@ -17,7 +17,10 @@ components:
|
|||
item:
|
||||
description: 'Item response'
|
||||
parameters:
|
||||
itemName:
|
||||
name: itemName
|
||||
item_name:
|
||||
name: item_name
|
||||
in: path
|
||||
description: 'The item name'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
|
@ -0,0 +1,26 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: 'Parameter Ref'
|
||||
version: 1.0.0
|
||||
paths:
|
||||
'/item/{item_name}':
|
||||
get:
|
||||
operationId: 6ecb3788642c6ba8ce8d99cbcd554dbe
|
||||
parameters:
|
||||
-
|
||||
$ref: '#/components/parameters/item_name'
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/components/responses/item'
|
||||
components:
|
||||
responses:
|
||||
item:
|
||||
description: 'Item response'
|
||||
parameters:
|
||||
item_name:
|
||||
name: item_name
|
||||
in: path
|
||||
description: 'The item name'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
|
@ -16,6 +16,7 @@ use OpenApi\Annotations as OA;
|
|||
use OpenApi\Context;
|
||||
use OpenApi\Analysers\TokenAnalyser;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Processors\MergeIntoOpenApi;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\AbstractLogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -84,6 +85,13 @@ class OpenApiTestCase extends TestCase
|
|||
};
|
||||
}
|
||||
|
||||
public function skipLegacy(): void
|
||||
{
|
||||
if ($this->getAnalyzer() instanceof TokenAnalyser) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
}
|
||||
|
||||
public function getContext(array $properties = [], ?string $version = OA\OpenApi::DEFAULT_VERSION): Context
|
||||
{
|
||||
return new Context(
|
||||
|
@ -235,7 +243,8 @@ class OpenApiTestCase extends TestCase
|
|||
|
||||
(new Generator($this->getTrackingLogger()))
|
||||
->setAnalyser($analyzer ?: $this->getAnalyzer())
|
||||
->setProcessors($processors)
|
||||
// run at least MergeIntoOpenApi to have a valid OpenApi version set
|
||||
->setProcessors($processors ?: [new MergeIntoOpenApi()])
|
||||
->generate($this->fixtures($files), $analysis, false);
|
||||
|
||||
return $analysis;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
namespace OpenApi\Tests\Processors;
|
||||
|
||||
use OpenApi\Analysers\TokenAnalyser;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Processors\ExpandEnums;
|
||||
|
@ -17,15 +16,16 @@ use OpenApi\Tests\Fixtures\PHP\Enums\StatusEnumStringBacked;
|
|||
use OpenApi\Tests\Fixtures\PHP\Enums\TypeEnumStringBacked;
|
||||
use OpenApi\Tests\OpenApiTestCase;
|
||||
|
||||
/**
|
||||
* @requires PHP 8.1
|
||||
*/
|
||||
class ExpandEnumsTest extends OpenApiTestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
if (PHP_VERSION_ID < 80100 || $this->getAnalyzer() instanceof TokenAnalyser) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
$this->skipLegacy();
|
||||
}
|
||||
|
||||
public function testExpandUnitEnum(): void
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
namespace OpenApi\Tests;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
|
||||
class ScratchTest extends OpenApiTestCase
|
||||
|
@ -19,11 +20,27 @@ class ScratchTest extends OpenApiTestCase
|
|||
continue;
|
||||
}
|
||||
|
||||
yield $name => [
|
||||
$this->fixture("Scratch/$name.php"),
|
||||
$this->fixture("Scratch/$name.yaml"),
|
||||
[],
|
||||
$scratch = $this->fixture("Scratch/$name.php");
|
||||
$specs = [
|
||||
$this->fixture("Scratch/{$name}3.1.0.yaml") => OA\OpenApi::VERSION_3_1_0,
|
||||
$this->fixture("Scratch/{$name}3.0.0.yaml") => OA\OpenApi::VERSION_3_0_0,
|
||||
];
|
||||
|
||||
$expectedLogs = [
|
||||
'Examples-3.0.0' => ['@OA\Schema() is only allowed for 3.1.0'],
|
||||
];
|
||||
|
||||
foreach ($specs as $spec => $version) {
|
||||
if (file_exists($spec)) {
|
||||
$dataSet = "$name-$version";
|
||||
yield $dataSet => [
|
||||
$scratch,
|
||||
$spec,
|
||||
$version,
|
||||
array_key_exists($dataSet, $expectedLogs) ? $expectedLogs[$dataSet] : [],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,21 +51,19 @@ class ScratchTest extends OpenApiTestCase
|
|||
*
|
||||
* @requires PHP 8.1
|
||||
*/
|
||||
public function testScratch(string $scratch, string $spec, array $expectedLog): void
|
||||
public function testScratch(string $scratch, string $spec, string $version, array $expectedLogs): void
|
||||
{
|
||||
foreach ($expectedLog as $logLine) {
|
||||
foreach ($expectedLogs as $logLine) {
|
||||
$this->assertOpenApiLogEntryContains($logLine);
|
||||
}
|
||||
|
||||
require_once $scratch;
|
||||
|
||||
$openapi = (new Generator($this->getTrackingLogger()))
|
||||
->setVersion($version)
|
||||
->generate([$scratch]);
|
||||
|
||||
if (!file_exists($spec)) {
|
||||
file_put_contents($spec, $openapi->toYaml());
|
||||
}
|
||||
|
||||
// file_put_contents($spec, $openapi->toYaml());
|
||||
$this->assertSpecEquals($openapi, file_get_contents($spec));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ class SerializerTest extends OpenApiTestCase
|
|||
$path->post->responses = [$resp, $respRange];
|
||||
|
||||
$expected = new OA\OpenApi(['_context' => $this->getContext()]);
|
||||
$expected->openapi = '3.0.0';
|
||||
$expected->openapi = OA\OpenApi::VERSION_3_0_0;
|
||||
$expected->paths = [
|
||||
$path,
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue