From 8f2c1ff650c62fa056ec74c5bf7f625e45b2bb8a Mon Sep 17 00:00:00 2001 From: Martin Rademacher Date: Mon, 8 Nov 2021 13:45:29 +1300 Subject: [PATCH] Attachables (#995) Introduce new annotation `Attachable` This allows to attach any data to a given annotation. Attachables are not part of the spec but they allow to inject custom data to annotations that can then be processed by a custom processor. --- Examples/misc/misc-api.php | 3 +- src/Annotations/AbstractAnnotation.php | 8 ++- src/Annotations/AdditionalProperties.php | 1 + src/Annotations/Attachable.php | 72 +++++++++++++++++++++++ src/Annotations/Components.php | 1 + src/Annotations/Contact.php | 7 +++ src/Annotations/Discriminator.php | 7 +++ src/Annotations/Examples.php | 7 +++ src/Annotations/ExternalDocumentation.php | 7 +++ src/Annotations/Flow.php | 7 +++ src/Annotations/Header.php | 1 + src/Annotations/Info.php | 1 + src/Annotations/Items.php | 1 + src/Annotations/JsonContent.php | 1 + src/Annotations/License.php | 7 +++ src/Annotations/Link.php | 1 + src/Annotations/MediaType.php | 2 + src/Annotations/OpenApi.php | 1 + src/Annotations/Operation.php | 1 + src/Annotations/Parameter.php | 1 + src/Annotations/PathItem.php | 1 + src/Annotations/Property.php | 1 + src/Annotations/RequestBody.php | 1 + src/Annotations/Response.php | 1 + src/Annotations/Schema.php | 1 + src/Annotations/SecurityScheme.php | 1 + src/Annotations/Server.php | 1 + src/Annotations/ServerVariable.php | 7 +++ src/Annotations/Tag.php | 1 + src/Annotations/Xml.php | 7 +++ src/Annotations/XmlContent.php | 1 + src/Serializer.php | 1 + tests/Annotations/AttachableTest.php | 40 +++++++++++++ tests/Annotations/CustomAttachable.php | 24 ++++++++ tests/Fixtures/UsingCustomAttachables.php | 20 +++++++ tests/Fixtures/UsingVar.php | 4 +- 36 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 src/Annotations/Attachable.php create mode 100644 tests/Annotations/AttachableTest.php create mode 100644 tests/Annotations/CustomAttachable.php create mode 100644 tests/Fixtures/UsingCustomAttachables.php diff --git a/Examples/misc/misc-api.php b/Examples/misc/misc-api.php index bcdbd4bf8..728d81df9 100644 --- a/Examples/misc/misc-api.php +++ b/Examples/misc/misc-api.php @@ -9,7 +9,8 @@ * securityScheme="bearerAuth", * type="http", * scheme="bearer", - * ) + * ), + * @OA\Attachable() * ) */ diff --git a/src/Annotations/AbstractAnnotation.php b/src/Annotations/AbstractAnnotation.php index 65d2de10b..3012ad834 100644 --- a/src/Annotations/AbstractAnnotation.php +++ b/src/Annotations/AbstractAnnotation.php @@ -26,6 +26,12 @@ abstract class AbstractAnnotation implements \JsonSerializable */ public $x = Generator::UNDEFINED; + /** + * Arbitrary attachables for this annotation. + * These will be ignored but can be used for custom processing. + */ + public $attachables = Generator::UNDEFINED; + /** * @var Context */ @@ -81,7 +87,7 @@ abstract class AbstractAnnotation implements \JsonSerializable * * @var array */ - public static $_blacklist = ['_context', '_unmerged']; + public static $_blacklist = ['_context', '_unmerged', 'attachables']; public function __construct(array $properties) { diff --git a/src/Annotations/AdditionalProperties.php b/src/Annotations/AdditionalProperties.php index 3921003b7..0cdf2113a 100644 --- a/src/Annotations/AdditionalProperties.php +++ b/src/Annotations/AdditionalProperties.php @@ -33,5 +33,6 @@ class AdditionalProperties extends Schema ExternalDocumentation::class => 'externalDocs', Xml::class => 'xml', AdditionalProperties::class => 'additionalProperties', + Attachable::class => ['attachables'], ]; } diff --git a/src/Annotations/Attachable.php b/src/Annotations/Attachable.php new file mode 100644 index 000000000..447f9e517 --- /dev/null +++ b/src/Annotations/Attachable.php @@ -0,0 +1,72 @@ + ['headers', 'header'], SecurityScheme::class => ['securitySchemes', 'securityScheme'], Link::class => ['links', 'link'], + Attachable::class => ['attachables'], ]; } diff --git a/src/Annotations/Contact.php b/src/Annotations/Contact.php index 9e6849e5e..03b445d9f 100644 --- a/src/Annotations/Contact.php +++ b/src/Annotations/Contact.php @@ -52,4 +52,11 @@ class Contact extends AbstractAnnotation public static $_parents = [ Info::class, ]; + + /** + * @inheritdoc + */ + public static $_nested = [ + Attachable::class => ['attachables'], + ]; } diff --git a/src/Annotations/Discriminator.php b/src/Annotations/Discriminator.php index 13117bd1c..479c7dc32 100644 --- a/src/Annotations/Discriminator.php +++ b/src/Annotations/Discriminator.php @@ -57,4 +57,11 @@ class Discriminator extends AbstractAnnotation JsonContent::class, XmlContent::class, ]; + + /** + * @inheritdoc + */ + public static $_nested = [ + Attachable::class => ['attachables'], + ]; } diff --git a/src/Annotations/Examples.php b/src/Annotations/Examples.php index 90dcd8224..57676008c 100644 --- a/src/Annotations/Examples.php +++ b/src/Annotations/Examples.php @@ -80,4 +80,11 @@ class Examples extends AbstractAnnotation JsonContent::class, XmlContent::class, ]; + + /** + * @inheritdoc + */ + public static $_nested = [ + Attachable::class => ['attachables'], + ]; } diff --git a/src/Annotations/ExternalDocumentation.php b/src/Annotations/ExternalDocumentation.php index 06557a26e..6cf04d631 100644 --- a/src/Annotations/ExternalDocumentation.php +++ b/src/Annotations/ExternalDocumentation.php @@ -65,4 +65,11 @@ class ExternalDocumentation extends AbstractAnnotation JsonContent::class, XmlContent::class, ]; + + /** + * @inheritdoc + */ + public static $_nested = [ + Attachable::class => ['attachables'], + ]; } diff --git a/src/Annotations/Flow.php b/src/Annotations/Flow.php index 5c6068e0d..aa9711bd8 100644 --- a/src/Annotations/Flow.php +++ b/src/Annotations/Flow.php @@ -79,6 +79,13 @@ class Flow extends AbstractAnnotation SecurityScheme::class, ]; + /** + * @inheritdoc + */ + public static $_nested = [ + Attachable::class => ['attachables'], + ]; + /** @inheritdoc */ #[\ReturnTypeWillChange] public function jsonSerialize() diff --git a/src/Annotations/Header.php b/src/Annotations/Header.php index 4d46d5486..c0992afff 100644 --- a/src/Annotations/Header.php +++ b/src/Annotations/Header.php @@ -80,6 +80,7 @@ class Header extends AbstractAnnotation */ public static $_nested = [ Schema::class => 'schema', + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/Info.php b/src/Annotations/Info.php index 06169ba3d..364227ac1 100644 --- a/src/Annotations/Info.php +++ b/src/Annotations/Info.php @@ -80,6 +80,7 @@ class Info extends AbstractAnnotation public static $_nested = [ Contact::class => 'contact', License::class => 'license', + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/Items.php b/src/Annotations/Items.php index c6fd95d3b..6e3d02a66 100644 --- a/src/Annotations/Items.php +++ b/src/Annotations/Items.php @@ -22,6 +22,7 @@ class Items extends Schema ExternalDocumentation::class => 'externalDocs', Xml::class => 'xml', AdditionalProperties::class => 'additionalProperties', + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/JsonContent.php b/src/Annotations/JsonContent.php index 8c796da64..db2c00163 100644 --- a/src/Annotations/JsonContent.php +++ b/src/Annotations/JsonContent.php @@ -42,5 +42,6 @@ class JsonContent extends Schema ExternalDocumentation::class => 'externalDocs', AdditionalProperties::class => 'additionalProperties', Examples::class => ['examples', 'example'], + Attachable::class => ['attachables'], ]; } diff --git a/src/Annotations/License.php b/src/Annotations/License.php index 54121be21..6e0bfd3cb 100644 --- a/src/Annotations/License.php +++ b/src/Annotations/License.php @@ -49,4 +49,11 @@ class License extends AbstractAnnotation public static $_parents = [ Info::class, ]; + + /** + * @inheritdoc + */ + public static $_nested = [ + Attachable::class => ['attachables'], + ]; } diff --git a/src/Annotations/Link.php b/src/Annotations/Link.php index 427e03a6a..11256fcc9 100644 --- a/src/Annotations/Link.php +++ b/src/Annotations/Link.php @@ -83,6 +83,7 @@ class Link extends AbstractAnnotation */ public static $_nested = [ Server::class => 'server', + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/MediaType.php b/src/Annotations/MediaType.php index fbaa55cd5..028a5cf4b 100644 --- a/src/Annotations/MediaType.php +++ b/src/Annotations/MediaType.php @@ -62,7 +62,9 @@ class MediaType extends AbstractAnnotation public static $_nested = [ Schema::class => 'schema', Examples::class => ['examples', 'example'], + Attachable::class => ['attachables'], ]; + /** * @inheritdoc */ diff --git a/src/Annotations/OpenApi.php b/src/Annotations/OpenApi.php index af8225516..91ebab81d 100644 --- a/src/Annotations/OpenApi.php +++ b/src/Annotations/OpenApi.php @@ -115,6 +115,7 @@ class OpenApi extends AbstractAnnotation Components::class => 'components', Tag::class => ['tags'], ExternalDocumentation::class => 'externalDocs', + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/Operation.php b/src/Annotations/Operation.php index 34390b15e..8bc6440b5 100644 --- a/src/Annotations/Operation.php +++ b/src/Annotations/Operation.php @@ -162,6 +162,7 @@ abstract class Operation extends AbstractAnnotation ExternalDocumentation::class => 'externalDocs', Server::class => ['servers'], RequestBody::class => 'requestBody', + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/Parameter.php b/src/Annotations/Parameter.php index 46de8e4a3..e3aabcef6 100644 --- a/src/Annotations/Parameter.php +++ b/src/Annotations/Parameter.php @@ -212,6 +212,7 @@ class Parameter extends AbstractAnnotation public static $_nested = [ Schema::class => 'schema', Examples::class => ['examples', 'example'], + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/PathItem.php b/src/Annotations/PathItem.php index 50af7d236..b1cd5b9b0 100644 --- a/src/Annotations/PathItem.php +++ b/src/Annotations/PathItem.php @@ -133,6 +133,7 @@ class PathItem extends AbstractAnnotation Options::class => 'options', Parameter::class => ['parameters'], Server::class => ['servers'], + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/Property.php b/src/Annotations/Property.php index 5c2b9f634..7532467b4 100644 --- a/src/Annotations/Property.php +++ b/src/Annotations/Property.php @@ -49,5 +49,6 @@ class Property extends Schema ExternalDocumentation::class => 'externalDocs', Xml::class => 'xml', AdditionalProperties::class => 'additionalProperties', + Attachable::class => ['attachables'], ]; } diff --git a/src/Annotations/RequestBody.php b/src/Annotations/RequestBody.php index 613dc5aca..3b094ff7c 100644 --- a/src/Annotations/RequestBody.php +++ b/src/Annotations/RequestBody.php @@ -80,5 +80,6 @@ class RequestBody extends AbstractAnnotation */ public static $_nested = [ MediaType::class => ['content', 'mediaType'], + Attachable::class => ['attachables'], ]; } diff --git a/src/Annotations/Response.php b/src/Annotations/Response.php index c9cc273f6..7562b2d41 100644 --- a/src/Annotations/Response.php +++ b/src/Annotations/Response.php @@ -83,6 +83,7 @@ class Response extends AbstractAnnotation MediaType::class => ['content', 'mediaType'], Header::class => ['headers', 'header'], Link::class => ['links', 'link'], + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/Schema.php b/src/Annotations/Schema.php index ca5f8e75b..8b8861522 100644 --- a/src/Annotations/Schema.php +++ b/src/Annotations/Schema.php @@ -357,6 +357,7 @@ class Schema extends AbstractAnnotation ExternalDocumentation::class => 'externalDocs', Xml::class => 'xml', AdditionalProperties::class => 'additionalProperties', + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/SecurityScheme.php b/src/Annotations/SecurityScheme.php index 552a9ef83..f972777d6 100644 --- a/src/Annotations/SecurityScheme.php +++ b/src/Annotations/SecurityScheme.php @@ -109,6 +109,7 @@ class SecurityScheme extends AbstractAnnotation */ public static $_nested = [ Flow::class => ['flows', 'flow'], + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/Server.php b/src/Annotations/Server.php index b825222a7..44a32b5cc 100644 --- a/src/Annotations/Server.php +++ b/src/Annotations/Server.php @@ -63,6 +63,7 @@ class Server extends AbstractAnnotation */ public static $_nested = [ ServerVariable::class => ['variables', 'serverVariable'], + Attachable::class => ['attachables'], ]; /** diff --git a/src/Annotations/ServerVariable.php b/src/Annotations/ServerVariable.php index 499b850dd..aeb3139f9 100644 --- a/src/Annotations/ServerVariable.php +++ b/src/Annotations/ServerVariable.php @@ -72,4 +72,11 @@ class ServerVariable extends AbstractAnnotation 'default' => 'string', 'description' => 'string', ]; + + /** + * @inheritdoc + */ + public static $_nested = [ + Attachable::class => ['attachables'], + ]; } diff --git a/src/Annotations/Tag.php b/src/Annotations/Tag.php index fc39b9205..ac87bf21b 100644 --- a/src/Annotations/Tag.php +++ b/src/Annotations/Tag.php @@ -61,5 +61,6 @@ class Tag extends AbstractAnnotation */ public static $_nested = [ ExternalDocumentation::class => 'externalDocs', + Attachable::class => ['attachables'], ]; } diff --git a/src/Annotations/Xml.php b/src/Annotations/Xml.php index f7c0eb050..edbb00b1c 100644 --- a/src/Annotations/Xml.php +++ b/src/Annotations/Xml.php @@ -72,4 +72,11 @@ class Xml extends AbstractAnnotation Items::class, XmlContent::class, ]; + + /** + * @inheritdoc + */ + public static $_nested = [ + Attachable::class => ['attachables'], + ]; } diff --git a/src/Annotations/XmlContent.php b/src/Annotations/XmlContent.php index 6fe654af1..67b57d7f3 100644 --- a/src/Annotations/XmlContent.php +++ b/src/Annotations/XmlContent.php @@ -37,5 +37,6 @@ class XmlContent extends Schema Xml::class => 'xml', AdditionalProperties::class => 'additionalProperties', Examples::class => ['examples', 'example'], + Attachable::class => ['attachables'], ]; } diff --git a/src/Serializer.php b/src/Serializer.php index c8039c3e7..7a09ad275 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -17,6 +17,7 @@ class Serializer { private static $VALID_ANNOTATIONS = [ OA\AdditionalProperties::class, + OA\Attachable::class, OA\Components::class, OA\Contact::class, OA\Delete::class, diff --git a/tests/Annotations/AttachableTest.php b/tests/Annotations/AttachableTest.php new file mode 100644 index 000000000..96dd4f7fc --- /dev/null +++ b/tests/Annotations/AttachableTest.php @@ -0,0 +1,40 @@ +analysisFromFixtures('UsingVar.php'); + + $schemas = $analysis->getAnnotationsOfType(Schema::class, true); + + $this->assertCount(2, $schemas[0]->attachables); + $this->assertInstanceOf(Attachable::class, $schemas[0]->attachables[0]); + } + + public function testCustomAttachableImplementationsAreAttached() + { + $analysis = new Analysis([], $this->getContext()); + (new Generator()) + //->setAliases(['oaf' => 'OpenApi\\Tests\\Annotations']) + ->setNamespaces(['OpenApi\\Tests\\Annotations\\']) + ->generate($this->fixtures('UsingCustomAttachables.php'), $analysis); + + $schemas = $analysis->getAnnotationsOfType(Schema::class, true); + + $this->assertCount(2, $schemas[0]->attachables); + $this->assertInstanceOf(CustomAttachable::class, $schemas[0]->attachables[0]); + } +} diff --git a/tests/Annotations/CustomAttachable.php b/tests/Annotations/CustomAttachable.php new file mode 100644 index 000000000..a435ff056 --- /dev/null +++ b/tests/Annotations/CustomAttachable.php @@ -0,0 +1,24 @@ +