Skip to content

Commit

Permalink
Allow finding child attributes in method args (#1368)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruscon authored Jan 4, 2023
1 parent 62ab286 commit 34aeee1
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 19 deletions.
8 changes: 4 additions & 4 deletions Examples/nesting/nesting.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,21 @@ components:
$ref: '#/components/schemas/AlmostModel'
-
properties:
actual:
type: string
soClose:
type: string
actual:
type: string
AlmostModel:
type: object
allOf:
-
$ref: '#/components/schemas/BaseModel'
-
properties:
almost:
type: string
intermediate:
type: string
almost:
type: string
BaseModel:
properties:
base:
Expand Down
5 changes: 3 additions & 2 deletions Examples/using-links-php81/RepositoriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public function getRepository()
public function getPullRequestsByRepository(
#[OAT\PathParameter()] string $username,
#[OAT\PathParameter()] string $slug,
#[OAT\PathParameter()] State $state
#[OAT\PathParameter()] State $state,
#[OAT\QueryParameter()] ?string $label
) {
}

Expand Down Expand Up @@ -102,7 +103,7 @@ public function getPullRequestsById()
)
]
#[OAT\Link(link: 'PullRequestMerge', operationId: 'mergePullRequest', parameters: ['username' => '$response.body#/author/username', 'slug' => '$response.body#/repository/slug', 'pid' => '$response.body#/id'])]
public function mergePullRequest()
public function mergePullRequest(#[OAT\HeaderParameter(name: 'X-NONCE-ID')] string $nonceId, #[OAT\CookieParameter(name: 'User-Bind-Session')] ?string $bindCookie)
{
}
}
18 changes: 18 additions & 0 deletions Examples/using-links-php81/using-links-php81.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ paths:
required: true
schema:
$ref: '#/components/schemas/State'
-
name: label
in: query
required: false
schema:
type: string
responses:
'200':
description: 'An array of pull request objects'
Expand Down Expand Up @@ -136,6 +142,18 @@ paths:
required: true
schema:
type: string
-
name: X-NONCE-ID
in: header
required: true
schema:
type: string
-
name: User-Bind-Session
in: cookie
required: false
schema:
type: string
responses:
'204':
description: 'The PR was successfully merged'
Expand Down
15 changes: 9 additions & 6 deletions src/Analysers/AttributeAnnotationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
namespace OpenApi\Analysers;

use OpenApi\Annotations as OA;
use OpenApi\Attributes as OAT;
use OpenApi\Context;
use OpenApi\Generator;

Expand Down Expand Up @@ -52,20 +51,24 @@ public function build(\Reflector $reflector, Context $context): array
if ($reflector instanceof \ReflectionMethod) {
// also look at parameter attributes
foreach ($reflector->getParameters() as $rp) {
foreach ([OAT\Property::class, OAT\Parameter::class, OAT\PathParameter::class] as $attributeName) {
foreach ($rp->getAttributes($attributeName) as $attribute) {
foreach ([OA\Property::class, OA\Parameter::class, OA\RequestBody::class] as $attributeName) {
foreach ($rp->getAttributes($attributeName, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
$instance = $attribute->newInstance();
$type = (($rnt = $rp->getType()) && $rnt instanceof \ReflectionNamedType) ? $rnt->getName() : Generator::UNDEFINED;
$nullable = $rnt ? $rnt->allowsNull() : true;

if ($instance instanceof OAT\Property) {
if ($instance instanceof OA\RequestBody) {
$instance->required = !$nullable;
} elseif ($instance instanceof OA\Property) {
$instance->property = $rp->getName();
if (Generator::isDefault($instance->type)) {
$instance->type = $type;
}
$instance->nullable = $nullable;
} else {
$instance->name = $rp->getName();
if (!$instance->name || Generator::isDefault($instance->name)) {
$instance->name = $rp->getName();
}
$instance->required = !$nullable;
$context = new Context(['nested' => $this], $context);
$context->comment = null;
Expand All @@ -78,7 +81,7 @@ public function build(\Reflector $reflector, Context $context): array

if (($rrt = $reflector->getReturnType()) && $rrt instanceof \ReflectionNamedType) {
foreach ($annotations as $annotation) {
if ($annotation instanceof OAT\Property && Generator::isDefault($annotation->type)) {
if ($annotation instanceof OA\Property && Generator::isDefault($annotation->type)) {
// pick up simple return types
$annotation->type = $rrt->getName();
}
Expand Down
20 changes: 20 additions & 0 deletions src/Annotations/CookieParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Annotations;

/**
* A `@OA\Request` cookie parameter.
*
* @Annotation
*/
class CookieParameter extends Parameter
{
/**
* @inheritdoc
*/
public $in = 'cookie';
}
20 changes: 20 additions & 0 deletions src/Annotations/HeaderParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Annotations;

/**
* A `@OA\Request` header parameter.
*
* @Annotation
*/
class HeaderParameter extends Parameter
{
/**
* @inheritdoc
*/
public $in = 'header';
}
20 changes: 20 additions & 0 deletions src/Annotations/QueryParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Annotations;

/**
* A `@OA\Request` query parameter.
*
* @Annotation
*/
class QueryParameter extends Parameter
{
/**
* @inheritdoc
*/
public $in = 'query';
}
16 changes: 16 additions & 0 deletions src/Attributes/CookieParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Attributes;

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
class CookieParameter extends Parameter
{
/**
* @inheritdoc
*/
public $in = 'cookie';
}
16 changes: 16 additions & 0 deletions src/Attributes/HeaderParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Attributes;

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
class HeaderParameter extends Parameter
{
/**
* @inheritdoc
*/
public $in = 'header';
}
12 changes: 10 additions & 2 deletions src/Attributes/PathParameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@
namespace OpenApi\Attributes;

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
class PathParameter extends \OpenApi\Annotations\PathParameter
class PathParameter extends Parameter
{
use ParameterTrait;
/**
* @inheritdoc
*/
public $in = 'path';

/**
* @inheritdoc
*/
public $required = true;
}
16 changes: 16 additions & 0 deletions src/Attributes/QueryParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Attributes;

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
class QueryParameter extends Parameter
{
/**
* @inheritdoc
*/
public $in = 'query';
}
5 changes: 3 additions & 2 deletions src/Attributes/RequestBody.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
namespace OpenApi\Attributes;

use OpenApi\Generator;
use OpenApi\Annotations as OA;

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PARAMETER)]
class RequestBody extends \OpenApi\Annotations\RequestBody
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER)]
class RequestBody extends OA\RequestBody
{
/**
* @param array<MediaType>|JsonContent|XmlContent|Attachable|null $content
Expand Down
3 changes: 3 additions & 0 deletions src/Serializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class Serializer
OA\Options::class,
OA\Parameter::class,
OA\PathParameter::class,
OA\QueryParameter::class,
OA\CookieParameter::class,
OA\HeaderParameter::class,
OA\Patch::class,
OA\PathItem::class,
OA\Post::class,
Expand Down
2 changes: 1 addition & 1 deletion tests/Annotations/AttributesSyncTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function testParameterCompleteness(string $annotation): void
}
}
// oh, well...
if ($attributeRC->isSubclassOf(OA\PathParameter::class)) {
if ($attributeRC->isSubclassOf(OA\Parameter::class)) {
// not relevant
unset($typeMismatch['in']);
// uses inheritdoc
Expand Down
4 changes: 2 additions & 2 deletions tests/Annotations/ValidateRelationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function testAncestors($class): void
foreach ($class::$_parents as $parent) {
$found = false;
foreach (array_keys($parent::$_nested) as $nestedClass) {
if ($nestedClass === $class) {
if ($nestedClass === $class || is_subclass_of($class, $nestedClass)) {
$found = true;
break;
}
Expand All @@ -45,7 +45,7 @@ public function testNested($class): void
foreach (array_keys($class::$_nested) as $nestedClass) {
$found = false;
foreach ($nestedClass::$_parents as $parent) {
if ($parent === $class) {
if ($parent === $class || is_subclass_of($class, $parent)) {
$found = true;
break;
}
Expand Down

0 comments on commit 34aeee1

Please sign in to comment.