Skip to content

Commit

Permalink
Add support for OpenApi 3.1.0 Webhooks (#1511)
Browse files Browse the repository at this point in the history
  • Loading branch information
DerManoMann authored Dec 11, 2023
1 parent 8ea52e3 commit 5087638
Show file tree
Hide file tree
Showing 16 changed files with 443 additions and 89 deletions.
12 changes: 7 additions & 5 deletions Examples/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ Collection of code/annotation examples and their corresponding OpenAPI specs gen
* misc: [source](misc) / [spec](misc/misc.yaml)
* using interfaces: [source](using-interfaces) / [spec](using-interfaces/using-interfaces.yaml)
* using traits: [source](using-traits) / [spec](using-traits/using-traits.yaml)
* using refs: [source](using-refs) / [spec](using-refs/using-refs.yaml)
* using refs: [source](using-refs) / [spec](using-refs/using-refs.yaml)
* nested schemas and class hierachies: [source](nesting) / [spec](nesting/nesting.yaml)
* polymorphism using `@OA\Discriminator`: [source](polymorphism) / [spec](polymorphism/polymorphism.yaml)

* webhooks using `@OA\Webhooks`: [source](webhooks) / [spec](webhooks/webhooks.yaml)
* webhooks81 using `@OAT\Webhooks`: [source](webhooks81) / [spec](webhooks81/webhooks.yaml)


## Custom processors

Expand All @@ -52,10 +54,10 @@ class MyCustomProcessor
{
public function __invoke(Analysis $analysis)
{
// custom processing
// custom processing
}
}
```
```

* **schema-query-parameter processor**

Expand All @@ -66,6 +68,6 @@ class MyCustomProcessor

* **sort-components processor**

A processor that sorts components so they appear in alphabetical order.
A processor that sorts components so they appear in alphabetical order.

[source](processors/sort-components)
34 changes: 34 additions & 0 deletions Examples/webhooks/OpenApiSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php declare(strict_types=1);

namespace OpenApi\Examples\Webhooks;

use OpenApi\Annotations as OA;

/**
* @OA\OpenApi(
* @OA\Info(
* version="1.0.0",
* title="Webhook Example"
* ),
* @OA\Webhook(
* webhook="newPet",
* @OA\Post(
* @OA\RequestBody(
* description="Information about a new pet in the system",
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(ref="#/components/schemas/Pet")
* )
* ),
* @OA\Response(
* response=200,
* description="Return a 200 status to indicate that the data was received successfully"
* )
* )
* )
* )
* )
*/
class OpenApiSpec
{
}
32 changes: 32 additions & 0 deletions Examples/webhooks/Pet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);

namespace OpenApi\Examples\Webhooks;

use OpenApi\Annotations as OA;

/**
* @OA\Schema(required={"id", "name"})
*/
final class Pet
{
/**
* @OA\Property(format="int64")
*
* @var int
*/
public $id;

/**
* @OA\Property
*
* @var string
*/
public $name;

/**
* @OA\Property
*
* @var string
*/
public $tag;
}
32 changes: 32 additions & 0 deletions Examples/webhooks/webhooks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
openapi: 3.1.0
info:
title: 'Webhook Example'
version: 1.0.0
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
type: object
webhooks:
newPet:
post:
operationId: 072580cbd56e3fef2b4c81536d3fd1c6
requestBody:
description: 'Information about a new pet in the system'
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
responses:
'200':
description: 'Return a 200 status to indicate that the data was received successfully'
32 changes: 32 additions & 0 deletions Examples/webhooks81/OpenApiSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);

namespace OpenApi\Examples\Webhooks81;

use OpenApi\Attributes as OAT;

#[OAT\OpenApi(
info: new OAT\Info(version: '1.0.0', title: 'Webhook Example'),
webhooks: [

This comment has been minimized.

Copy link
@afilina

afilina Feb 29, 2024

This does not seem to work out-of-the-box. I tracked this down to a missing
openapi: \OpenApi\Annotations\OpenApi::VERSION_3_1_0, under OAT\OpenApi. Without it, the lib defaults to VERSION_3_0_0, which according to src/Annotations/OpenApi.php, silently ignores webhooks this when creating the spec file.

It would also be useful to validate the webhooks and only allow it if openapi is at the correct version, as this is extremely hard to figure out for the uninitiated.

This comment has been minimized.

Copy link
@DerManoMann

DerManoMann Mar 1, 2024

Author Collaborator

Agreed. The way the code deals with different versions is not great at all.

new OAT\Webhook(
webhook: 'newPet',
post: new OAT\Post(
requestBody: new OAT\RequestBody(
description: 'Information about a new pet in the system',
content: new OAT\MediaType(
mediaType: 'application/json',
schema: new OAT\Schema(ref: Pet::class)
)
),
responses: [
new OAT\Response(
response: 200,
description: 'Return a 200 status to indicate that the data was received successfully'
),
]
)
),
],
)]
class OpenApiSpec
{
}
18 changes: 18 additions & 0 deletions Examples/webhooks81/Pet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types=1);

namespace OpenApi\Examples\Webhooks81;

use OpenApi\Attributes as OAT;

#[OAT\Schema(required: ['id', 'name'])]
final class Pet
{
#[OAT\Property(format: 'int64')]
public int $id;

#[OAT\Property]
public string $name;

#[OAT\Property]
public string $tag;
}
32 changes: 32 additions & 0 deletions Examples/webhooks81/webhooks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
openapi: 3.1.0
info:
title: 'Webhook Example'
version: 1.0.0
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
type: object
webhooks:
newPet:
post:
operationId: bbbe318bf00166ae6ba3552197e5f089
requestBody:
description: 'Information about a new pet in the system'
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
responses:
'200':
description: 'Return a 200 status to indicate that the data was received successfully'
1 change: 1 addition & 0 deletions src/Annotations/Attachable.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class Attachable extends AbstractAnnotation
ServerVariable::class,
Tag::class,
Trace::class,
Webhook::class,
Xml::class,
XmlContent::class,
];
Expand Down
40 changes: 39 additions & 1 deletion src/Annotations/OpenApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ class OpenApi extends AbstractAnnotation
*/
public $externalDocs = Generator::UNDEFINED;

/**
* The available webhooks for the API.
*
* @var Webhook[]
*/
public $webhooks = Generator::UNDEFINED;

/**
* @var Analysis
*/
Expand All @@ -106,7 +113,7 @@ class OpenApi extends AbstractAnnotation
/**
* @inheritdoc
*/
public static $_required = ['openapi', 'info', 'paths'];
public static $_required = ['openapi', 'info'];

/**
* @inheritdoc
Expand All @@ -118,6 +125,7 @@ class OpenApi extends AbstractAnnotation
Components::class => 'components',
Tag::class => ['tags'],
ExternalDocumentation::class => 'externalDocs',
Webhook::class => ['webhooks', 'webhook'],
Attachable::class => ['attachables'],
];

Expand All @@ -143,6 +151,21 @@ public function validate(array $stack = null, array $skip = null, string $ref =
return false;
}

/* paths is optional in 3.1.0 */
if ($this->openapi === self::VERSION_3_0_0 && Generator::isDefault($this->paths)) {
$this->_context->logger->warning('Required @OA\PathItem() not found');
}

if ($this->openapi === self::VERSION_3_1_0
&& Generator::isDefault($this->paths)
&& Generator::isDefault($this->webhooks)
&& Generator::isDefault($this->components)
) {
$this->_context->logger->warning("At least one of 'Required @OA\PathItem(), @OA\Components() or @OA\Webhook() not found'");

return false;
}

return parent::validate([], [], '#', new \stdClass());
}

Expand Down Expand Up @@ -230,4 +253,19 @@ private static function resolveRef(string $ref, string $resolved, $container, ar

throw new \Exception('$ref "' . $unresolved . '" not found');
}

/**
* @inheritdoc
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
$data = parent::jsonSerialize();

if (false === $this->_context->isVersion(OpenApi::VERSION_3_1_0)) {
unset($data->webhooks);
}

return $data;
}
}
43 changes: 43 additions & 0 deletions src/Annotations/Webhook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Annotations;

use OpenApi\Generator;

/**
* Acts like a `PathItem` with the main difference being that it requires `webhook` instead of `path`.
*
* @Annotation
*/
class Webhook extends PathItem
{
/**
* Key for the webhooks map.
*
* @var string
*/
public $webhook = Generator::UNDEFINED;

/**
* @inheritdoc
*/
public static $_required = ['webhook'];

/**
* @inheritdoc
*/
public static $_parents = [
OpenApi::class,
];

/**
* @inheritdoc
*/
public static $_types = [
'webhook' => 'string',
];
}
4 changes: 3 additions & 1 deletion src/Attributes/OpenApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class OpenApi extends \OpenApi\Annotations\OpenApi
* @param Server[]|null $servers
* @param Tag[]|null $tags
* @param PathItem[]|null $paths
* @param Webhook[]|null $webhooks
* @param array<string,mixed>|null $x
* @param Attachable[]|null $attachables
*/
Expand All @@ -27,6 +28,7 @@ public function __construct(
?ExternalDocumentation $externalDocs = null,
?array $paths = null,
?Components $components = null,
?array $webhooks = null,
// annotation
?array $x = null,
?array $attachables = null
Expand All @@ -35,7 +37,7 @@ public function __construct(
'openapi' => $openapi,
'security' => $security ?? Generator::UNDEFINED,
'x' => $x ?? Generator::UNDEFINED,
'value' => $this->combine($info, $servers, $tags, $externalDocs, $paths, $components, $attachables),
'value' => $this->combine($info, $servers, $tags, $externalDocs, $paths, $components, $webhooks, $attachables),
]);
}
}
Loading

0 comments on commit 5087638

Please sign in to comment.