New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problem with scan and apply custom (extended) attributes #1416
Comments
Changes seem reasonable, in particular if the (roughly) same works for other custom attributes. Would you be able to compress som eof the code into a single file example that reproduces the issue? Multiple classes in the file would be fine, this is just to keep it simple (and perhaps add to the test suite) |
@roquie I've created a PR (#1423) 1423that adds a scratch test that tries to reproduce your custom attributes. The result so far is looking good to me. If you have a little time to try to add to that so it breaks that would be most helpful. Without a simple reproducable testcase there isn't a lot I can (or am willing) to do. |
In fact, 4.7.3 has a related change that might also be fixing your problem. |
No, latest release is not fixing this problem. May be problem with scan files feature? |
Ok, back to square one. Would you be able to create a single file repoducer to help debugging? |
Sorry, I'm late... Filename: <?php
/**
* @license Apache 2.0
*/
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use OpenApi\Attributes as OA;
use OpenApi\Generator;
#[OA\Info(version: '1.0.0', title: 'API')]
class App {}
#[\Attribute(
\Attribute::TARGET_CLASS |
\Attribute::TARGET_METHOD |
\Attribute::TARGET_PROPERTY |
\Attribute::IS_REPEATABLE
)]
class Schema extends OA\Schema
{
/**
* @param class-string $of
* @param string|null $description
* @param array $optional
* @param int|null $minLength
* @param int|null $maxLength
* @throws \ReflectionException
*/
public function __construct(
string $of,
?string $description = null,
array $optional = [],
?int $minLength = null,
?int $maxLength = null,
) {
$class = new \ReflectionClass($of);
$required = null;
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
if (\in_array($property->getName(), $optional, true)) {
continue;
}
$required[] = $property->getName();
}
parent::__construct(
schema: $of,
title: $class->getShortName(),
description: $description,
required: $required,
maxLength: $maxLength,
minLength: $minLength
);
}
}
#[\Attribute(
\Attribute::TARGET_METHOD |
\Attribute::TARGET_PROPERTY |
\Attribute::TARGET_PARAMETER |
\Attribute::TARGET_CLASS_CONSTANT |
\Attribute::IS_REPEATABLE
)]
class Collection extends OA\Property
{
/** @param class-string $of */
public function __construct(
string $of,
?string $description = null
) {
parent::__construct(
title: $of,
description: $description,
items: new OA\Items(ref: "#/components/schemas/$of")
);
}
}
#[\Attribute(
\Attribute::TARGET_METHOD |
\Attribute::TARGET_PROPERTY |
\Attribute::TARGET_PARAMETER |
\Attribute::TARGET_CLASS_CONSTANT |
\Attribute::IS_REPEATABLE
)]
class Item extends OA\Property
{
/** @param class-string $of */
public function __construct(
string $of,
?string $description = null
) {
parent::__construct(
ref: "#/components/schemas/$of",
title: $of,
description: $description,
);
}
}
#[\Attribute(
\Attribute::TARGET_METHOD |
\Attribute::TARGET_PROPERTY |
\Attribute::TARGET_PARAMETER |
\Attribute::TARGET_CLASS_CONSTANT |
\Attribute::IS_REPEATABLE
)]
class Raw extends OA\Property
{
public function __construct(
?string $title = null,
?string $description = null
) {
parent::__construct(
title: $title,
description: $description,
);
}
}
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Successful extends OA\Response
{
/** @param ?class-string $of */
public function __construct(
?string $of = null,
) {
if ($of === null) {
parent::__construct(
response: 200,
description: 'Operation complete'
);
return;
}
parent::__construct(
response: 200,
description: "Successful response of [$of]",
content: new OA\JsonContent(
ref: "#/components/schemas/$of"
)
);
}
}
final class TargetGroupController
{
#[
OA\Get(path: '/target_groups', summary: 'List target groups', tags: ['Target groups']),
Successful(of: TargetGroupListDto::class)
]
public function list(): string {}
}
#[Schema(of: TargetGroupListDto::class)]
final class TargetGroupListDto
{
public function __construct(
/** @var TargetGroupDto[] */
#[Collection(of: TargetGroupDto::class)]
public readonly array $targetGroups = []
) {}
}
#[Schema(of: TargetGroupDto::class)]
final class TargetGroupDto
{
public function __construct(
#[OA\Property] # with my custom attribute #[Item] I also had problems
public readonly string $groupId,
#[OA\Property] # with my custom attribute #[Item] I also had problems
public readonly string $groupName,
/** @var TargetDto[] */
#[Collection(of: TargetDto::class)]
// #[OA\Property( # WORKS
// items: new OA\Items(ref: '#/components/schemas/TargetDto')
// )]
public readonly array $targets = []
) {}
}
#[Schema(of: TargetDto::class)]
final class TargetDto
{
public function __construct(
#[Item(of: TargetId::class)]
public readonly string $targetId,
#[Item(of: TargetType::class)]
public readonly string $targetType,
// ...
) {}
}
#[Schema(of: TargetId::class)]
class TargetId {}
#[Schema(of: TargetType::class)]
class TargetType {}
$generator = Generator::scan([
__DIR__ . '/test-openapi3-attr.php',
]);
echo $generator->toYaml(); P.S. For main problem you can uncomment 184-186 lines |
PHP 8.2/8.1
|
Sweet, thanks for that. |
Question - is it ok to add your sample code as test case to the project? It would get the same Apache license header ideally....
|
Of course, it’s not a problem |
Yeah! It works! Tested right now. |
FR: Can you add option to generator to choose enum type values for spec? I can create a new issue for that. use App\Common\Interfaces\Http\Documentation\Schema;
#[Schema(of: HttpMethodType::class)]
enum HttpMethodType: string
{
case Head = 'HEAD';
case Get = 'GET';
case Post = 'POST';
case Put = 'PUT';
case Patch = 'PATCH';
case Delete = 'DELETE';
case Options = 'OPTIONS';
} but may be developer wants to use enum case values, like: |
Have a look here: https://zircote.github.io/swagger-php/guide/common-techniques.html#enum-cases-as-value Setting the type explicitly to |
type: 'string' helps me, thx :) |
I want to extend default attributes and then use it in the project. I do this like:
When I using my attributes I got an "silent error" with nesting DTO classes. DTO structure be like:
... and for this structure library generate following openapi schema:
Library do not generate for me nested array items if I using my custom
#[Collection()]
attribute. If I comment my attribute and using#[OA\Property(items: ...)]
directly it works fine. But my attribute is a pure-simple and this behavior is unexpected...The text was updated successfully, but these errors were encountered: