Add test to ensure attribute parameters are matching annotations (#1217)
This commit is contained in:
parent
fd0d164fb7
commit
6e13ebbeba
|
@ -7,4 +7,5 @@ coverage/
|
|||
docs/.vitepress/dist/
|
||||
docs/node_modules/
|
||||
docs/package-lock.json
|
||||
scratch/
|
||||
vendor/
|
||||
|
|
|
@ -21,7 +21,7 @@ abstract class AbstractAnnotation implements \JsonSerializable
|
|||
* For further details see https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#specificationExtensions
|
||||
* The keys inside the array will be prefixed with `x-`.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string,string>
|
||||
*/
|
||||
public $x = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ class Components extends AbstractAnnotation
|
|||
/**
|
||||
* Reusable Callbacks.
|
||||
*
|
||||
* @var array
|
||||
* @var callable[]
|
||||
*/
|
||||
public $callbacks = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class Examples extends AbstractAnnotation
|
|||
* To represent examples of media types that cannot naturally be represented
|
||||
* in JSON or YAML, use a string value to contain the example, escaping where necessary.
|
||||
*
|
||||
* @var string
|
||||
* @var int|string|array
|
||||
*/
|
||||
public $value = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -57,6 +57,8 @@ class Flow extends AbstractAnnotation
|
|||
* The available scopes for the OAuth2 security scheme.
|
||||
*
|
||||
* A map between the scope name and a short description for it.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scopes = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class Header extends AbstractAnnotation
|
|||
/**
|
||||
* Schema object.
|
||||
*
|
||||
* @var \OpenApi\Annotations\Schema
|
||||
* @var Schema
|
||||
*/
|
||||
public $schema = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -69,6 +69,8 @@ class Link extends AbstractAnnotation
|
|||
* be evaluated and passed to the linked operation.
|
||||
* The parameter name can be qualified using the parameter location [{in}.]{name} for operations
|
||||
* that use the same parameter name in different locations (e.g. path.id).
|
||||
*
|
||||
* @var array<string,mixed>
|
||||
*/
|
||||
public $parameters = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class MediaType extends AbstractAnnotation
|
|||
* Furthermore, if referencing a schema which contains an example,
|
||||
* the examples value shall override the example provided by the schema.
|
||||
*
|
||||
* @var Examples[]
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
|
@ -64,7 +64,7 @@ class MediaType extends AbstractAnnotation
|
|||
* The encoding object shall only apply to requestBody objects when the media type is multipart or
|
||||
* application/x-www-form-urlencoded.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string,mixed>
|
||||
*/
|
||||
public $encoding = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class OpenApi extends AbstractAnnotation
|
|||
*
|
||||
* This is not related to the API info::version string.
|
||||
*
|
||||
* @var string|null
|
||||
* @var string
|
||||
*/
|
||||
public $openapi = self::DEFAULT_VERSION;
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ abstract class Operation extends AbstractAnnotation
|
|||
/**
|
||||
* The list of possible responses as they are returned from executing this operation.
|
||||
*
|
||||
* @var \OpenApi\Annotations\Response[]
|
||||
* @var Response[]
|
||||
*/
|
||||
public $responses = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ class Parameter extends AbstractAnnotation
|
|||
* 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
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class PathItem extends AbstractAnnotation
|
|||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* key for the Path Object (OpenApi->paths array).
|
||||
* Key for the Path Object (OpenApi->paths array).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
|
|
@ -17,6 +17,11 @@ use OpenApi\Generator;
|
|||
*/
|
||||
class RequestBody extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
|
|
|
@ -64,7 +64,7 @@ class Response extends AbstractAnnotation
|
|||
* For responses that match multiple keys, only the most specific key is applicable;
|
||||
* e.g. <code>text/plain</code> overrides <code>text/*</code>.
|
||||
*
|
||||
* @var MediaType[]
|
||||
* @var MediaType|JsonContent|XmlContent|array<MediaType|JsonContent|XmlContent>
|
||||
*/
|
||||
public $content = Generator::UNDEFINED;
|
||||
|
||||
|
@ -74,7 +74,7 @@ class Response extends AbstractAnnotation
|
|||
* The key of the map is a short name for the link, following the naming constraints of the names for Component
|
||||
* Objects.
|
||||
*
|
||||
* @var array
|
||||
* @var Link[]
|
||||
*/
|
||||
public $links = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ class Schema extends AbstractAnnotation
|
|||
/**
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor17)
|
||||
*
|
||||
* @var number
|
||||
* @var int|float
|
||||
*/
|
||||
public $maximum = Generator::UNDEFINED;
|
||||
|
||||
|
@ -143,7 +143,7 @@ class Schema extends AbstractAnnotation
|
|||
/**
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor21)
|
||||
*
|
||||
* @var number
|
||||
* @var int|float
|
||||
*/
|
||||
public $minimum = Generator::UNDEFINED;
|
||||
|
||||
|
@ -199,7 +199,7 @@ class Schema extends AbstractAnnotation
|
|||
/**
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor76)
|
||||
*
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
public $enum = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class Server extends AbstractAnnotation
|
|||
*
|
||||
* The value is used for substitution in the server's URL template.
|
||||
*
|
||||
* @var array
|
||||
* @var ServerVariable[]
|
||||
*/
|
||||
public $variables = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ use OpenApi\Generator;
|
|||
class XmlContent extends Schema
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
|
|
|
@ -12,13 +12,21 @@ use OpenApi\Generator;
|
|||
class AdditionalProperties extends \OpenApi\Annotations\AdditionalProperties
|
||||
{
|
||||
/**
|
||||
* @param string[]|null $required
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[] $enum
|
||||
* @param Schema[] $allOf
|
||||
* @param Schema[] $anyOf
|
||||
* @param Schema[] $oneOf
|
||||
* @param mixed $const
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
|
|
|
@ -19,9 +19,9 @@ class Examples extends \OpenApi\Annotations\Examples
|
|||
?string $example = null,
|
||||
?string $summary = null,
|
||||
?string $description = null,
|
||||
string|array|null $value = null,
|
||||
int|string|array|null $value = null,
|
||||
?string $externalValue = null,
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
|
|
|
@ -15,7 +15,7 @@ class Header extends \OpenApi\Annotations\Header
|
|||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $header = null,
|
||||
?string $description = null,
|
||||
?bool $required = null,
|
||||
|
|
|
@ -14,12 +14,19 @@ class Items extends \OpenApi\Annotations\Items
|
|||
/**
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[] $enum
|
||||
* @param Schema[] $allOf
|
||||
* @param Schema[] $anyOf
|
||||
* @param Schema[] $oneOf
|
||||
* @param mixed $const
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
|
|
|
@ -12,16 +12,22 @@ use OpenApi\Generator;
|
|||
class JsonContent extends \OpenApi\Annotations\JsonContent
|
||||
{
|
||||
/**
|
||||
* @param array<string,Examples> $examples
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[] $enum
|
||||
* @param Schema[] $allOf
|
||||
* @param Schema[] $anyOf
|
||||
* @param Schema[] $oneOf
|
||||
* @param mixed $const
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?array $examples = null,
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
|
|
|
@ -18,20 +18,27 @@ class Link extends \OpenApi\Annotations\Link
|
|||
*/
|
||||
public function __construct(
|
||||
?string $link = null,
|
||||
string|object|null $ref = null,
|
||||
?string $operationRef = null,
|
||||
string|null $ref = null,
|
||||
?string $operationId = null,
|
||||
?array $parameters = null,
|
||||
$requestBody = null,
|
||||
?string $description = null,
|
||||
?Server $server = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'link' => $link ?? Generator::UNDEFINED,
|
||||
'operationRef' => $operationRef ?? Generator::UNDEFINED,
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'operationId' => $operationId ?? Generator::UNDEFINED,
|
||||
'parameters' => $parameters ?? Generator::UNDEFINED,
|
||||
'requestBody' => $requestBody ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables),
|
||||
'value' => $this->combine($server, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ class MediaType extends \OpenApi\Annotations\MediaType
|
|||
{
|
||||
/**
|
||||
* @param array<string,Examples> $examples
|
||||
* @param array<string,mixed> $encoding
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
|
@ -21,7 +22,7 @@ class MediaType extends \OpenApi\Annotations\MediaType
|
|||
?Schema $schema = null,
|
||||
$example = Generator::UNDEFINED,
|
||||
?array $examples = null,
|
||||
?string $encoding = null,
|
||||
?array $encoding = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
|
|
|
@ -14,6 +14,7 @@ class OpenApi extends \OpenApi\Annotations\OpenApi
|
|||
/**
|
||||
* @param Server[]|null $servers
|
||||
* @param Tag[]|null $tags
|
||||
* @param PathItem[]|null $paths
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
|
@ -24,6 +25,8 @@ class OpenApi extends \OpenApi\Annotations\OpenApi
|
|||
?array $security = null,
|
||||
?array $tags = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
?array $paths = null,
|
||||
?Components $components = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
|
@ -32,7 +35,7 @@ class OpenApi extends \OpenApi\Annotations\OpenApi
|
|||
'openapi' => $openapi,
|
||||
'security' => $security ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($info, $servers, $tags, $externalDocs, $attachables),
|
||||
'value' => $this->combine($info, $servers, $tags, $externalDocs, $paths, $components, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ trait OperationTrait
|
|||
* @param string[] $tags
|
||||
* @param Parameter[] $parameters
|
||||
* @param Response[] $responses
|
||||
* @param callable[] $callbacks
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,7 @@ trait ParameterTrait
|
|||
{
|
||||
/**
|
||||
* @param array<string,Examples> $examples
|
||||
* @param MediaType[] $content
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
|
@ -21,11 +22,17 @@ trait ParameterTrait
|
|||
?string $description = null,
|
||||
?string $in = null,
|
||||
?bool $required = null,
|
||||
string|object|null $ref = null,
|
||||
?bool $deprecated = null,
|
||||
?bool $allowEmptyValue = null,
|
||||
string|null $ref = null,
|
||||
?Schema $schema = null,
|
||||
$example = Generator::UNDEFINED,
|
||||
?array $examples = null,
|
||||
?string $style = null,
|
||||
?bool $explode = null,
|
||||
?bool $allowReserved = null,
|
||||
?array $spaceDelimited = null,
|
||||
?array $pipeDelimited = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
|
@ -36,9 +43,15 @@ trait ParameterTrait
|
|||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'in' => Generator::isDefault($this->in) ? $in : $this->in,
|
||||
'required' => !Generator::isDefault($this->required) ? $this->required : ($required ?? Generator::UNDEFINED),
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'allowEmptyValue' => $allowEmptyValue ?? Generator::UNDEFINED,
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'example' => $example,
|
||||
'style' => $style ?? Generator::UNDEFINED,
|
||||
'explode' => $explode ?? Generator::UNDEFINED,
|
||||
'allowReserved' => $allowReserved ?? Generator::UNDEFINED,
|
||||
'spaceDelimited' => $spaceDelimited ?? Generator::UNDEFINED,
|
||||
'pipeDelimited' => $pipeDelimited ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($schema, $examples, $attachables),
|
||||
]);
|
||||
|
|
|
@ -12,17 +12,27 @@ use OpenApi\Generator;
|
|||
class PathItem extends \OpenApi\Annotations\PathItem
|
||||
{
|
||||
/**
|
||||
* @param Server[]|null $servers
|
||||
* @param Parameter[]|null $parameters
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $path = null,
|
||||
?string $summary = null,
|
||||
?array $servers = null,
|
||||
?array $parameters = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'path' => $path ?? Generator::UNDEFINED,
|
||||
'summary' => $summary ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables),
|
||||
'value' => $this->combine($servers, $parameters, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//Missing parameters: get, put, post, delete, options, head, patch, trace, parameters
|
||||
|
|
|
@ -14,13 +14,20 @@ class Property extends \OpenApi\Annotations\Property
|
|||
/**
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[] $enum
|
||||
* @param Schema[] $allOf
|
||||
* @param Schema[] $anyOf
|
||||
* @param Schema[] $oneOf
|
||||
* @param mixed $const
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $property = null,
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
|
|
|
@ -17,7 +17,7 @@ class RequestBody extends \OpenApi\Annotations\RequestBody
|
|||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $request = null,
|
||||
?string $description = null,
|
||||
?bool $required = null,
|
||||
|
|
|
@ -6,23 +6,27 @@
|
|||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Annotations\JsonContent;
|
||||
use OpenApi\Annotations\MediaType;
|
||||
use OpenApi\Annotations\XmlContent;
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Response extends \OpenApi\Annotations\Response
|
||||
{
|
||||
/**
|
||||
* @param Header[] $headers
|
||||
* @param Link[] $links
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
* @param Header[] $headers
|
||||
* @param MediaType|JsonContent|XmlContent|array<MediaType|JsonContent|XmlContent> $content
|
||||
* @param Link[] $links
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
int|string $response = null,
|
||||
?string $description = null,
|
||||
?array $headers = null,
|
||||
$content = null,
|
||||
MediaType|JsonContent|XmlContent|array|null $content = null,
|
||||
?array $links = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
|
|
|
@ -13,13 +13,20 @@ class Schema extends \OpenApi\Annotations\Schema
|
|||
{
|
||||
/**
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[] $enum
|
||||
* @param Schema[] $allOf
|
||||
* @param Schema[] $anyOf
|
||||
* @param Schema[] $oneOf
|
||||
* @param mixed $const
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
|
|
|
@ -17,7 +17,7 @@ class SecurityScheme extends \OpenApi\Annotations\SecurityScheme
|
|||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $securityScheme = null,
|
||||
?string $type = null,
|
||||
?string $description = null,
|
||||
|
|
|
@ -12,6 +12,7 @@ use OpenApi\Generator;
|
|||
class ServerVariable extends \OpenApi\Annotations\ServerVariable
|
||||
{
|
||||
/**
|
||||
* @param string[]|null $enum
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
|
|
|
@ -14,14 +14,21 @@ class XmlContent extends \OpenApi\Annotations\XmlContent
|
|||
/**
|
||||
* @param array<string,Examples> $examples
|
||||
* @param string[] $required
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param Property[] $properties
|
||||
* @param string[] $enum
|
||||
* @param Schema[] $allOf
|
||||
* @param Schema[] $anyOf
|
||||
* @param Schema[] $oneOf
|
||||
* @param mixed $const
|
||||
* @param array<string,string>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?array $examples = null,
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
string|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace OpenApi\Tests\Annotations;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Tests\OpenApiTestCase;
|
||||
|
||||
/**
|
||||
* @requires PHP 8.1
|
||||
*/
|
||||
class AttributesSyncTest extends OpenApiTestCase
|
||||
{
|
||||
public static $SCHEMA_EXCLUSIONS = ['const', 'maxProperties', 'minProperties', 'multipleOf', 'not', 'additionalItems', 'contains', 'patternProperties', 'dependencies', 'propertyNames'];
|
||||
public static $PATHITEM_EXCLUSIONS = ['ref', 'get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
|
||||
public static $PARAMETER_EXCLUSIONS = ['content', 'matrix', 'label', 'form', 'simple', 'deepObject'];
|
||||
|
||||
public function testCounts()
|
||||
{
|
||||
$this->assertSameSize($this->allAnnotationClasses(), $this->allAttributeClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider allAnnotationClasses
|
||||
*/
|
||||
public function testParameterCompleteness($annotation): void
|
||||
{
|
||||
$annotationRC = new \ReflectionClass($annotation);
|
||||
$attributeRC = new \ReflectionClass('OpenApi\\Attributes\\' . $annotationRC->getShortName());
|
||||
$attributeCtor = $attributeRC->getMethod('__construct');
|
||||
$attributeParameters = $attributeCtor->getParameters();
|
||||
|
||||
$missing = [];
|
||||
$typeMismatch = [];
|
||||
foreach ($annotationRC->getProperties() as $property) {
|
||||
$propertyName = $property->getName();
|
||||
if (in_array($propertyName, $annotation::$_blacklist) || $propertyName[0] == '_') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$found = false;
|
||||
foreach ($attributeParameters as $attributeParameter) {
|
||||
if ($attributeParameter->getName() == $propertyName) {
|
||||
$annotationType = $this->propertyType($property);
|
||||
$attributeType = $this->parameterType($propertyName, $attributeParameter);
|
||||
|
||||
if ($annotationType != $attributeType) {
|
||||
$typeMismatch[$propertyName] = [$annotationType, $attributeType];
|
||||
}
|
||||
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// oh, well...
|
||||
if ($attributeRC->isSubclassOf(OA\PathParameter::class)) {
|
||||
// not relevant
|
||||
unset($typeMismatch['in']);
|
||||
// uses inheritdoc
|
||||
unset($typeMismatch['required']);
|
||||
}
|
||||
if (!$found) {
|
||||
// exclusions...
|
||||
if ($attributeRC->isSubclassOf(OA\Operation::class) && 'method' == $propertyName) {
|
||||
continue;
|
||||
}
|
||||
if ($attributeRC->isSubclassOf(OA\Attachable::class) && 'x' == $propertyName) {
|
||||
continue;
|
||||
}
|
||||
if ($attributeRC->isSubclassOf(OA\AdditionalProperties::class) && 'additionalProperties' == $propertyName) {
|
||||
continue;
|
||||
}
|
||||
if (in_array($propertyName, static::$SCHEMA_EXCLUSIONS)) {
|
||||
continue;
|
||||
}
|
||||
if ($attributeRC->isSubclassOf(OA\PathItem::class) && in_array($propertyName, static::$PATHITEM_EXCLUSIONS)) {
|
||||
continue;
|
||||
}
|
||||
if ($attributeRC->isSubclassOf(OA\Parameter::class) && in_array($propertyName, static::$PARAMETER_EXCLUSIONS)) {
|
||||
continue;
|
||||
}
|
||||
$missing[] = $propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
if ($missing) {
|
||||
$this->fail('Missing parameters: ' . implode(', ', $missing));
|
||||
}
|
||||
|
||||
if ($typeMismatch) {
|
||||
var_dump($typeMismatch);
|
||||
$this->fail('Type mismatch: ' . count($typeMismatch));
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepDocComment($docComment): array
|
||||
{
|
||||
if (!$docComment) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$lines = preg_split('/(\n|\r\n)/', $docComment);
|
||||
$lines[0] = preg_replace('/[ \t]*\\/\*\*/', '', $lines[0]); // strip '/**'
|
||||
$i = count($lines) - 1;
|
||||
$lines[$i] = preg_replace('/\*\/[ \t]*$/', '', $lines[$i]); // strip '*/'
|
||||
|
||||
foreach ($lines as $ii => $line) {
|
||||
$lines[$ii] = ltrim($line, "\t *");
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
protected function propertyType(\ReflectionProperty $property): ?string
|
||||
{
|
||||
$var = null;
|
||||
foreach ($this->prepDocComment($property->getDocComment()) as $line) {
|
||||
if (substr($line, 0, 1) === '@') {
|
||||
if (substr($line, 0, 5) === '@var ') {
|
||||
$var = trim(substr($line, 5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($var) {
|
||||
$var = str_replace(['OpenApi\\Annotations\\', 'OpenApi\\Attributes\\'], '', $var);
|
||||
if (false === strpos($var, '<')) {
|
||||
$var = explode('|', $var);
|
||||
sort($var);
|
||||
$var = implode('|', $var);
|
||||
}
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
|
||||
protected function parameterType(string $parameterName, \ReflectionParameter $parameter): ?string
|
||||
{
|
||||
$var = null;
|
||||
if ($type = $parameter->getType()) {
|
||||
if ($type instanceof \ReflectionUnionType) {
|
||||
$var = [];
|
||||
foreach ($type->getTypes() as $unionType) {
|
||||
if ('null' != $unionType->getName()) {
|
||||
// null means default for most parameters
|
||||
$var[] = $unionType->getName();
|
||||
}
|
||||
}
|
||||
sort($var);
|
||||
$var = implode('|', $var);
|
||||
} else {
|
||||
$var = $type->getName();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->prepDocComment($parameter->getDeclaringFunction()->getDocComment()) as $line) {
|
||||
if (substr($line, 0, 1) === '@') {
|
||||
if (substr($line, 0, 7) === '@param ') {
|
||||
$line = preg_replace('/ +/', ' ', $line);
|
||||
$token = explode(' ', trim(substr($line, 7)));
|
||||
if (2 == count($token)) {
|
||||
[$type, $name] = $token;
|
||||
if (str_replace('$', '', $name) == $parameterName) {
|
||||
$var = str_replace(['|null', 'null|'], '', $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($var) {
|
||||
$var = str_replace(['OpenApi\\Annotations\\', 'OpenApi\\Attributes\\'], '', $var);
|
||||
if (false === strpos($var, '<')) {
|
||||
$var = explode('|', $var);
|
||||
sort($var);
|
||||
$var = implode('|', $var);
|
||||
}
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider allAttributeClasses
|
||||
*/
|
||||
public function testPropertyCompleteness($attribute)
|
||||
{
|
||||
$attributeRC = new \ReflectionClass($attribute);
|
||||
$annotationRC = new \ReflectionClass('OpenApi\\Annotations\\' . $attributeRC->getShortName());
|
||||
$attributeCtor = $attributeRC->getMethod('__construct');
|
||||
|
||||
$stale = [];
|
||||
foreach ($attributeCtor->getParameters() as $parameter) {
|
||||
$parameterName = $parameter->getName();
|
||||
|
||||
if (!$annotationRC->hasProperty($parameterName)) {
|
||||
// exclusions...
|
||||
if ($attributeRC->isSubclassOf(OA\Attachable::class) && 'properties' == $parameterName) {
|
||||
continue;
|
||||
}
|
||||
$stale[] = $parameterName;
|
||||
}
|
||||
}
|
||||
|
||||
if ($stale) {
|
||||
$this->fail('Stale parameters: ' . implode(', ', $stale));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -268,4 +268,27 @@ class OpenApiTestCase extends TestCase
|
|||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect list of all non-abstract attribute classes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function allAttributeClasses(): array
|
||||
{
|
||||
$classes = [];
|
||||
$dir = new DirectoryIterator(__DIR__ . '/../src/Attributes');
|
||||
foreach ($dir as $entry) {
|
||||
if (!$entry->isFile() || $entry->getExtension() != 'php') {
|
||||
continue;
|
||||
}
|
||||
$class = $entry->getBasename('.php');
|
||||
if (in_array($class, ['OperationTrait', 'ParameterTrait'])) {
|
||||
continue;
|
||||
}
|
||||
$classes[$class] = ['OpenApi\\Attributes\\' . $class];
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue