-
Notifications
You must be signed in to change notification settings - Fork 937
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
Custom processor for creating annotations programatically #1592
Comments
I have a similar situation. In particular, I want to avoid polluting my source code with information that already exists. E.g. endpoint paths are already defined in the router (configuration), so it doesn't make sense to add annotations to controllers/endpoints. // routes.php
$router->get('x/y', Controllers\X\Y::class);
// Controllers/X/Y.php
/**
* Returns data for Y. <-- Simple PHPDoc that should show up as OA endpoint description.
* To add more OA-specific details, I would just need to add OA-specific attributes/annotations
*/
class Y {
public function __invoke(): Responses\Y|Responses\NotFound
// ^ if we follow the thought that this method will never cause an exit/die and unhandled exceptions trigger a 500
// server error, then we can reasonably assuming that there will only be 3 distinct types of responses.
{
$model = $this->repo->findModel();
return $model
? new Responses\Y($model)
: new Responses\NotFound()
}
}
// Responses/NotFound.php
class NotFound extends Response {
public const STATUS_CODE = 404;
// ^ obviously, to make this work correctly, I would need to teach swagger-php the meaning of this class and that constant
// In theory, I should be able to support more complex cases, such as JsonAPI with similar PHP-native(or-almost) code (e.g. array shapes in PHPDoc etc).
} In the beginning I tried subclassing attributes, hoping that a custom Then I tried implementing my own analyser (since in theory I don't need to analyse the entire codebase - I can start from my routes). But that also didn't work out - I couldn't figure out how Analysis are being merged. Then I tried to jump the whole flow and implement my own generator (and skip the step of having an analyser). Now that I think about it, it's possible a processor could have been enough (the generator approach is still a work in progress and quite painful - right now I'm stuck as to why the object hierarchy I'm generating is causing conflicting components). Anyway, the main annoyance is perhaps lack of documentation or a clear public interface for extension (not complaining though, it is a pretty complex subject on the whole). |
FYI: I've moved most of the generator code into a processor (and used the stock generator/analyser), and somehow that solved those problems. |
Can you share your code? |
Sure! I also found today that you can use Attribute classes too (they're nicer because their constructor defines parameters separately, instead of just passing an array). Good to know: The attribute classes still extend the original annotation classes. The 'gotcha' is that there is some sort of hack that you need to enable first, so I did that in a base processor and then I define my own processors extending the base one. // AbstractProcessor.php
use OpenApi;
abstract class AbstractProcessor implements OpenApi\Processors\ProcessorInterface
{
final public function __invoke(OpenApi\Analysis $analysis): void
{
OpenApi\Generator::$context = $analysis->context;
try {
$this->process($analysis);
} finally {
OpenApi\Generator::$context = null;
}
}
abstract protected function process(OpenApi\Analysis $analysis): void;
}
// ComposerAppInfo.php
use OpenApi;
use Override;
class ComposerAppInfo extends AbstractProcessor
{
public function __construct(
readonly private string $composerConfigFile
) {
}
#[Override] protected function process(OpenApi\Analysis $analysis): void
{
$config = file_get_contents($this->composerConfigFile);
$config = json_decode($config, false, 512, JSON_THROW_ON_ERROR);
$analysis->addAnnotation(
new OpenApi\Attributes\Info( // <-- in your case, you could have a PathItem (and child elements) here
version: $config->version ?? null,
description: $config->description ?? null,
title: array_reverse(explode('/', $config->name))[0],
contact: $config->homepage ?? null,
license: new OpenApi\Attributes\License(
name: $config->license ?? null,
),
),
$analysis->context
);
}
}
// Wherever you want to generate the schema (e.g. in some console command)
// (or additionally you can use the swagger-php cli with the custom processors option, but I haven't tried that yet)
$generator = new OpenApi\Generator();
$generator->setProcessors([
new Processors\ComposerAppInfo(__DIR__ . '/../../../composer.json'),
...$generator->getProcessors() // <- add back original processors
]);
$openapi = $generator->generate([__DIR__ . '/../src']); I have some other code that defines the whole PathItem/Operations/Responses hierarchy, which seems to be what you need. It's not too difficult through the attribute classes. |
I have a route that looks like this
Now, I want to write a custom processor that automatically adds the corresponding
OA\Operation
annotation. For a later step, it'd be my goal to addOA\Response
annotations based on the $this->createResponse() calls.I tried the following:
However, it seems like the
addAnnotation
is not the correct method to use here. What would be the correct approach to this?The text was updated successfully, but these errors were encountered: