vyuldashev / laravel-openapi Goto Github PK
View Code? Open in Web Editor NEWGenerate OpenAPI specification for Laravel Applications
License: MIT License
Generate OpenAPI specification for Laravel Applications
License: MIT License
While using php artisan openapi:generate
we can run into this exception:
This happens when your response class doesn't extend from Vyuldashev\LaravelOpenApi\Factories\ResponseFactory
or straight up doesn't exist in the path you specified in the annotation of your controller method.
My suggestion would be to specify the Factory that generated this error in the exception message so it can help future devs debug their code.
I have created multiple collection and set different url to them. But when I open any route I'm getting only default schema data
Hello,
I've ran into a problem today. While trying to generate the openapi file I had the following error:
TypeResolver is a dependency of ReflectionDocBlock, which is required by this package, I'm not sure if this is an issue related to this package or more on the side of ReflectionDocBlock but I decided to leave it here, maybe this saves other people some time aswell.
This exception only occurs on version 1.7 of TypeResolver, it works just fine on version 1.6.2. Since the update happened just 3 days ago I assume it has to be from the changes related to that.
These changes include a complete refactor of the TypeResolver.php which seems to cause this exception for me.
A quick fix is to add "phpdocumentor/type-resolver": "1.6.2"
to your own composer.json.
Hello,
I'm wondering if it is possible to add some argument to \Vyuldashev\LaravelOpenApi\Attributes\Parameters to allow some argument. The idea would be to pass the validation request, by example, and allow to build part of the parameter information
So something like this
#[OpenApi\Parameters(MyParameters::class), MyRequest::class]
#[OpenApi\Response(ErrorValidationResponse::class, 422)]
#[OpenApi\Operation]
public function index(MyRequest $request)
thanks
Within Laravel's config/database.php
file, there is an option to add a prefix to database table names.
My MySQL config for example (defualt prefix is '') :
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => env('DB_PREFIX', ''),
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
However, when generating a new schema with php artisan openapi:make-schema User -m User
, it looks like the prefix field isn't being honoured as it should be, as I get this error message:
Doctrine\DBAL\Schema\SchemaException
There is no column with name 'id' on table 'users'.
at vendor/doctrine/dbal/src/Schema/SchemaException.php:87
83▕ * @return SchemaException
84▕ */
85▕ public static function columnDoesNotExist($columnName, $table)
86▕ {
➜ 87▕ return new self(
88▕ sprintf("There is no column with name '%s' on table '%s'.", $columnName, $table),
89▕ self::COLUMN_DOESNT_EXIST
90▕ );
91▕ }
+3 vendor frames
4 [internal]:0
Vyuldashev\LaravelOpenApi\Console\SchemaFactoryMakeCommand::Vyuldashev\LaravelOpenApi\Console\{closure}("id")
+17 vendor frames
22 artisan:37
Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
My users table is actually prefixed with "auth_" so if it looked for "auth_users" it would find the correct structure.
I've looked at the code to see if I can submit a PR for this, but I can't seem to find on a quick glance, where that table name is set within the code.
TL;DR: Just read the answer if you want to know how to solve a CORS issue with swagger-ui when you're running laravel with open-api in a docker container.
Thanks for making this package, which I got installed without a problem.
I really like the PHP8 Attribute convention-over-configuration approach and want to stick to your package.
Problem:
I want to let a docker-container of the image swaggerapi/swagger-ui
run besides the Laravel-container.
The URL of the swagger-ui-container is: http://127.0.0.1:8082
.
The URL of your openapi-package within Laravel is: http://127.0.0.1:8080/openapi
.
When trying to explore http://127.0.0.1:8080/openapi
from the swagger-ui the following error-message is rendered in the browser:
"Possible cross-origin (CORS) issue? The URL origin (http://127.0.0.1:8080) does not match the page (http://127.0.0.1:8082). Check the server returns the correct 'Access-Control-Allow-*' headers".
In the network-log of general Laravel-API-Methods, the response-headers always include a Access-Control-Allow-Origin: *
-header.
I'm using "fruitcake/laravel-cors" for that.
In the network-log of your provided URL http://127.0.0.1:8080/openapi
the Access-Control-Allow-Origin: *
header is missing.
How can I enable that header for your URL?
In config\openapi.php
I tried:
'route' => [
'uri' => '/openapi',
'middleware' => [
\Fruitcake\Cors\HandleCors::class,
],
],
Hi,
I have such issues with multiple parameter as follow:
I code route with 3 parameter, but cannot generate the doc.
Route::prefix('activity')->group(function () {
Route::GET('/{activity}/{personalflag}/{houseid}',
[
'as' => 'activity.show',
'uses' => 'ActivityController@show',
]);
});
Below is the error that I got while using 3 parameter at route.
Symfony\Component\Debug\Exception\FatalThrowableError : Argument 1 passed to Vyuldashev\LaravelOpenApi\SchemaHelpers::guessFromReflectionType() must be an instance of ReflectionType, null given, called in D:\xampp7.3.0\htdocs\laravel\layanesia\vendor\vyuldashev\laravel-openapi\src\Builders\Paths\Operation\ParametersBuilder.php on line 39
at D:\xampp7.3.0\htdocs\laravel\layanesia\vendor\vyuldashev\laravel-openapi\src\SchemaHelpers.php:10
6| use ReflectionType;
7|
8| class SchemaHelpers
9| {
> 10| public static function guessFromReflectionType(ReflectionType $reflectionType): Schema
11| {
12| switch ($reflectionType->getName()) {
13| case 'int':
14| return Schema::integer();
Exception trace:
1 Vyuldashev\LaravelOpenApi\SchemaHelpers::guessFromReflectionType()
D:\xampp7.3.0\htdocs\laravel\layanesia\vendor\vyuldashev\laravel-openapi\src\Builders\Paths\Operation\ParametersBuilder.php:39
2 Vyuldashev\LaravelOpenApi\Builders\Paths\Operation\ParametersBuilder::Vyuldashev\LaravelOpenApi\Builders\Paths\Operation\{closure}(["houseid"])
[internal]:0
Please use the argument -v to see more details.
I'm using Laravel 8 and
php artisan openapi:make-schema User -m User command is not working correctly.
because of new namespace for Models App\Models\User
i fixed this in my project by adding 1 line in SchemaFactoryMakeCommad class.
protected function buildModel($output, $model)
{
$model = Str::start($model, $this->laravel->getNamespace());
if (! is_a($model, Model::class, true)) {
throw new InvalidArgumentException('Invalid model');
}
protected function buildModel($output, $model)
{
$namespace = app()::VERSION[0] >= 8 ? $this->laravel->getNamespace(). 'Models\\' : $this->laravel->getNamespace();
$model = Str::start($model, $namespace);
if (! is_a($model, Model::class, true)) {
throw new InvalidArgumentException('Invalid model');
}
My bad...
Compiling header parameters into a DeviceController
parameter file as it seems only a single parameter annotation is allowed on the route:
class DeviceParameters extends ParametersFactory
{
/**
* @return Parameter[]
*/
public function build(): array
{
return [
AcceptParameters::ref(),
ApiVersionParameters::ref(),
];
}
}
And here's the example ApiVersionParameters:
class ApiVersionParameters extends ParametersFactory implements Reusable
{
/**
* @return Parameter[]
*/
public function build(): array
{
return [
Parameter::header('ApiVersion')
->name('Api-Version')
->description('Api Version')
->required(true)
->schema(Schema::string()->example('1.0')),
];
}
}
Then receive this error on compile:
Trying to get property 'objectId' of non-object
at vendor/vyuldashev/laravel-openapi/src/Concerns/Referencable.php:41
37▕ } elseif ($instance instanceof SecuritySchemeFactory) {
38▕ $baseRef = '#/components/securitySchemes/';
39▕ }
40▕
➜ 41▕ return Schema::ref($baseRef.$instance->build()->objectId, $objectId);
42▕ }
43▕ }
44▕
+1 vendor frames
2 app/OpenApi/Parameters/DeviceParameters.php:21
Vyuldashev\LaravelOpenApi\Factories\ParametersFactory::ref()
+4 vendor frames
7 [internal]:0
Vyuldashev\LaravelOpenApi\Builders\PathsBuilder::Vyuldashev\LaravelOpenApi\Builders\{closure}(Object(Illuminate\Support\Col
lection), "/device/upgrade-token")
Which makes sense, the Parameters stub returns an array of parameters, but the static ref() is expecting an object.
Perhaps I'm approaching this wrong, open to alternatives! Thanks
Looking for documentation on how to extend an existing reusable schema.
Would the ideal solution be to to use openapi $ref and then additional properties?
Our server is configured as follows: `https://domain.com/api/v1'
However, this package seems to simply take the uri's from the route and use them as paths.
That means that the openapi document now contains operations for api/v1/blog/{blog}
whereas it should have been blog/{blog}
.
It feels to me the package should instead generate full url's for routes, and remove the server url prefix from them.
Incompatible with Laravel 10
- Root composer.json requires vyuldashev/laravel-openapi ^1.8 -> satisfiable by vyuldashev/laravel-openapi[v1.8.0].
- vyuldashev/laravel-openapi v1.8.0 requires laravel/framework 5.8.*|^6.0|^7.0|^8.0|^9.0 -> found laravel/framework[v5.8.0, ..., 5.8.x-dev, v6.0.0, ..., 6.x-dev, v7.0.0, ..., 7.x-dev, v8.0.0, ..., 8.x-dev, v9.0.0-beta.1, ..., 9.x-dev] but it conflicts with your root composer.json require (^10.0).
Hi there,
I've just installed this package and published It's configuration.
For some reason, when I try to generate the documentation I get the following error:
php artisan openapi:generate
TypeError
Argument 1 passed to Vyuldashev\LaravelOpenApi\Generator::__construct() must be of the type array, null given, called in C:\wamp64\www\Ocasiao-CMS\vendor\vyuldashev\laravel-openapi\src\OpenApiServiceProvider.php on line 47
at C:\wamp64\www\Ocasiao-CMS\vendor\vyuldashev\laravel-openapi\src\Generator.php:26
22▕ protected $tagsBuilder;
23▕ protected $pathsBuilder;
24▕ protected $componentsBuilder;
25▕
➜ 26▕ public function __construct(
27▕ array $config,
28▕ InfoBuilder $infoBuilder,
29▕ ServersBuilder $serversBuilder,
30▕ TagsBuilder $tagsBuilder,
1 C:\wamp64\www\Ocasiao-CMS\vendor\vyuldashev\laravel-openapi\src\OpenApiServiceProvider.php:47
Vyuldashev\LaravelOpenApi\Generator::__construct(Object(Vyuldashev\LaravelOpenApi\Builders\InfoBuilder), Object(Vyuldashev\LaravelOpenApi\Builders\ServersBuilder), Object(Vyuldashev\LaravelOpenApi\Builders\TagsBuilder), Object(Vyuldashev\LaravelOpenApi\Builders\PathsBuilder), Object(Vyuldashev\LaravelOpenApi\Builders\ComponentsBuilder))
2 C:\wamp64\www\Ocasiao-CMS\vendor\laravel\framework\src\Illuminate\Container\Container.php:805
Vyuldashev\LaravelOpenApi\OpenApiServiceProvider::Vyuldashev\LaravelOpenApi\{closure}(Object(Illuminate\Foundation\Application), [])
Can anyone help?
Used packages:
Dev:
The package roave/better-reflection has no version/tag 4.* so it uses dev-master for installation and this is the result:
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Conclusion: remove vyuldashev/laravel-openapi v0.18.1
- vyuldashev/laravel-openapi v0.18.1 requires roave/better-reflection ^4.0 -> satisfiable by roave/better-reflection[4.0.x-dev].
- Conclusion: remove roave/better-reflection 4.0.x-dev|keep phpdocumentor/reflection-docblock 4.3.4
- Conclusion: remove phpdocumentor/reflection-docblock 4.3.4
- Conclusion: remove phpdocumentor/type-resolver 1.0.1
- Conclusion: don't install phpdocumentor/type-resolver 1.0.1
- Conclusion: don't install phpdocumentor/reflection-docblock 4.3.4|don't install roave/better-reflection 4.0.x-dev|install phpdocumentor/reflection-docblock 5.0.0
- Conclusion: don't install phpdocumentor/reflection-docblock 5.0.0
- Conclusion: don't install phpdocumentor/reflection-docblock 4.3.3
- Installation request for vyuldashev/laravel-openapi ^0.18.1 -> satisfiable by vyuldashev/laravel-openapi[v0.18.1].
- roave/better-reflection 4.0.x-dev requires phpdocumentor/reflection-docblock ^5.0.0 -> satisfiable by phpdocumentor/reflection-docblock[5.0.0, 5.0.0-alpha1, 5.0.0-alpha2, 5.0.0-alpha3, 5.0.0-alpha4, 5.0.0-alpha5, 5.0.0-alpha6, 5.0.0-alpha7, 5.0.0-alpha8, 5.0.0-alpha9, 5.0.0-beta, 5.x-dev].
- Can only install one of: phpdocumentor/reflection-docblock[5.0.0-alpha6, 4.3.2].
- Can only install one of: phpdocumentor/reflection-docblock[5.0.0-alpha7, 4.3.2].
- Can only install one of: phpdocumentor/reflection-docblock[5.0.0-alpha8, 4.3.2].
- Can only install one of: phpdocumentor/reflection-docblock[5.0.0-alpha9, 4.3.2].
- Can only install one of: phpdocumentor/reflection-docblock[5.0.0-beta, 4.3.2].
- Can only install one of: phpdocumentor/reflection-docblock[5.x-dev, 4.3.2].
- vyuldashev/laravel-openapi v0.18.1 requires phpdocumentor/reflection-docblock ^4.3 -> satisfiable by phpdocumentor/reflection-docblock[4.3.4, 4.3.0, 4.3.1, 4.3.2, 4.3.3].
- phpdocumentor/reflection-docblock 4.3.0 requires phpdocumentor/type-resolver ^0.4.0 -> satisfiable by phpdocumentor/type-resolver[0.4.0].
- phpdocumentor/reflection-docblock 4.3.1 requires phpdocumentor/type-resolver ^0.4.0 -> satisfiable by phpdocumentor/type-resolver[0.4.0].
- phpdocumentor/reflection-docblock 5.0.0-alpha1 requires phpdocumentor/type-resolver ^0 -> satisfiable by phpdocumentor/type-resolver[0.1, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 0.2, 0.2.1, 0.3.0, 0.4.0, 0.5.0, 0.5.1, 0.6.0, 0.6.1, 0.6.2, 0.6.3, 0.7.0, 0.7.1, 0.7.2, 0.7.x-dev].
- phpdocumentor/reflection-docblock 5.0.0-alpha2 requires phpdocumentor/type-resolver ^0 -> satisfiable by phpdocumentor/type-resolver[0.1, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 0.2, 0.2.1, 0.3.0, 0.4.0, 0.5.0, 0.5.1, 0.6.0, 0.6.1, 0.6.2, 0.6.3, 0.7.0, 0.7.1, 0.7.2, 0.7.x-dev].
- phpdocumentor/reflection-docblock 5.0.0-alpha3 requires phpdocumentor/type-resolver ^0 -> satisfiable by phpdocumentor/type-resolver[0.1, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 0.2, 0.2.1, 0.3.0, 0.4.0, 0.5.0, 0.5.1, 0.6.0, 0.6.1, 0.6.2, 0.6.3, 0.7.0, 0.7.1, 0.7.2, 0.7.x-dev].
- phpdocumentor/reflection-docblock 5.0.0-alpha4 requires phpdocumentor/type-resolver ^0 -> satisfiable by phpdocumentor/type-resolver[0.1, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 0.2, 0.2.1, 0.3.0, 0.4.0, 0.5.0, 0.5.1, 0.6.0, 0.6.1, 0.6.2, 0.6.3, 0.7.0, 0.7.1, 0.7.2, 0.7.x-dev].
- phpdocumentor/reflection-docblock 5.0.0-alpha5 requires phpdocumentor/type-resolver ^0 -> satisfiable by phpdocumentor/type-resolver[0.1, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 0.2, 0.2.1, 0.3.0, 0.4.0, 0.5.0, 0.5.1, 0.6.0, 0.6.1, 0.6.2, 0.6.3, 0.7.0, 0.7.1, 0.7.2, 0.7.x-dev].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.4.0].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.5.0].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.5.1].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.6.0].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.6.1].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.6.2].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.6.3].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.7.0].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.7.1].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.7.2].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.7.x-dev].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1.1].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1.2].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1.3].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1.4].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1.5].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1.6].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1.7].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.1.8].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.2].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.2.1].
- Can only install one of: phpdocumentor/type-resolver[1.x-dev, 0.3.0].
- roave/better-reflection 4.0.x-dev requires phpdocumentor/type-resolver ^1.0.0 -> satisfiable by phpdocumentor/type-resolver[1.0.1, 1.0.0, 1.x-dev].
- Conclusion: don't install phpdocumentor/type-resolver 1.0.0
My packages at all:
"require": {
"php": ">=7.3",
"ext-json": "*",
"bensampo/laravel-enum": "^1.26",
"bschmitt/laravel-amqp": "^2.0",
"fideloper/proxy": "^4.0",
"laravel/framework": "^6.0",
"laravel/tinker": "^1.0",
"league/flysystem-aws-s3-v3": "^1.0",
"pion/laravel-chunk-upload": "^1.3",
"predis/predis": "^1.1",
"renepardon/amqp-pubsub": "^1.0",
"vladimir-yuldashev/laravel-queue-rabbitmq": "^10.1",
"vyuldashev/laravel-openapi": "^0.18.1"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^2.6",
"brainmaestro/composer-git-hooks": "^2.8",
"facade/ignition": "^1.4",
"fzaninotto/faker": "^1.4",
"haydenpierce/class-finder": "^0.4.0",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^3.0",
"nunomaduro/larastan": "^0.4.3",
"pdepend/pdepend": "^2.5",
"phing/phing": "^2.16",
"phploc/phploc": "^5.0",
"phpmd/phpmd": "^2.7",
"phpunit/php-code-coverage": "^7.0",
"phpunit/phpunit": "^8.0",
"renepardon/laravel-code-generator": "^1.0",
"renepardon/laravel-code-generator-swagger": "^1.0",
"sebastian/phpcpd": "^4.1",
"squizlabs/php_codesniffer": "^3.5"
},
For anyone wondering how to migrate the docblocks into PHP8 Attributes quickly, this one worked for me:
regex replace: \* @OA\\Operation\(tags=(.*?)\)\n.*?\*/
to */\n#[OA\\Operation(tags: [$1])]
\* @OA\\PathItem\(\)\n .*?\/
to */\n#[OA\\PathItem()]
Maybe it would be a good idea to include this (or maybe even better some modified version that would work in all cases) in a readme/doc somewhere?
Hello @vyuldashev 🤝
Thanks for making this excellent package. I wanted to make a quick issue to track the progress towards supporting OpenAPI v3.1, because it's been out for over a year now and it's excellent. It adds a lot of convenient functionality and lines up perfectly with JSON Schema, which could actually simplify your toolchain rather well as you can just use any of the vast amounts of JSON Schema tooling instead of the subset of tooling which understands what an OpenAPI Schema object.
Here's a guide to the differences written for end-users, but it should help make things fairly clear to tooling vendors like yourself.
https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0
If you say you'll be working on it I can update openapi.tools to reflect that.
Let me know if you have any questions or need any help with the upgrade process,
When the Laravel\Sanctum\Http\Controllers\CsrfCookieController
is used, there is no way to add it path into schema.
Route list ignore this Controller, because it not contains #[OpenApi\PathItem]
and #[OpenApi\Operation]
attributes, but I can't add them there, because it is a part of third-party package.
I have to make my own extension of that controller, also part of code, that automagicaly register them to route and so one...
It will be useful to have a possibility to setup this attributes also externally in the openapi.php
config file.
Hi there,
it would be great if PHP 8 could be supported here please.
Is anyone working on it?
thanks
If a Controller function accesses the path variable by model binding (as described here https://laravel.com/docs/9.x/routing#implicit-binding) and doesn't manually specify a path type, it is typed as string.
I would suggest to automatically try to type the path variable like the primary key type of the model
Change the guessFromReflectionType
of the SchemaHelpers
class to something like this:
public static function guessFromReflectionType(ReflectionType $reflectionType): Schema
{
$typeString = $reflectionType->getName();
if (is_subclass_of($reflectionType->getName(), 'Illuminate\Database\Eloquent\Model')) {
$modelInstance = new ($reflectionType->getName())();
$typeString = $modelInstance->getKeyType();
}
switch ($typeString) {
case 'int':
return Schema::integer();
case 'bool':
return Schema::boolean();
}
return Schema::string();
}
Hi,
When I try to make a composer require vyuldashev/laravel-openapi
I have the following conflict error :
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Can only install one of: phpdocumentor/type-resolver[1.0.1, 0.4.0].
- Can only install one of: phpdocumentor/type-resolver[0.4.0, 1.0.1].
- Can only install one of: phpdocumentor/type-resolver[0.4.0, 1.0.1].
- roave/better-reflection 3.5.0 requires phpdocumentor/type-resolver ^0.4.0 -> satisfiable by phpdocumentor/type-resolver[0.4.0].
- vyuldashev/laravel-openapi v0.7.0 requires roave/better-reflection ^3.5 -> satisfiable by roave/better-reflection[3.5.0].
- Installation request for vyuldashev/laravel-openapi ^0.7.0 -> satisfiable by vyuldashev/laravel-openapi[v0.7.0].
- Installation request for phpdocumentor/type-resolver (locked at 1.0.1) -> satisfiable by phpdocumentor/type-resolver[1.0.1].
Installation failed, reverting ./composer.json to its original content.
Can you check and tell me what's going on ?
Maybe it's since the last update for Laravel 6 ?
Thanks for you answer !
I'm trying to define Responses for my Open API Schema.
In my API, all of my controllers use Laravel Resources to define how models are transformed into responses, and which fields are included. So I have, say, 39 Controllers and 18 Resources.
Currently, I now need to define 18 Responses in Laravel OpenAPI.
However, it seems that if I could alter my UserResource
class, and simply add a trait, use OpenApiResponse
, then I could keep information about my response in the same place.
This would seem to simplify my project, in the same way that every endpoint is defined by a #[OpenApi\Operation(tags: ['User'])]
annotation on the controller, I could simply add some annotations and a trait on my resources.
P.S.: Love this tool, it's helping us out a lot.
In documentation:
"Even if the schema defines a status code, you must supply the status code in the controller method attributes, or only one response will be included in the result."
Even if I define a custom status code in controller level it's just ignored
Great package!
Missing documentation for Collections and Middlewares.
Can we have a rudimentary description for these and how they're used?
The only information we have is a comment in the config for the middlewares but it isn't much help.
In the first example code block here: https://vyuldashev.github.io/laravel-openapi/paths/operations.html#operations. The trait used is:
use Vyuldashev\LaravelOpenApi\Attributes as OpenApi;
but should be
use Vyuldashev\LaravelOpenApi\Annotations as OpenApi;
Right now the package has the functionality to create multiple collections, but there is no option to serve them on multiple routes.
Example:
/api/public/docs
/api/private/docs
Because OpenApiController does not receive a collection flag and uses a "default" one.
class OpenApiController
{
public function show(Generator $generator): OpenApi
{
return $generator->generate();
}
}
There should be a way to pass collection:
public function show(Generator $generator): OpenApi
{
return $generator->generate($collectionName);
}
Or it should take from config.
I can help, but which way would be preferable? Or add a custom controller setting to the config?
hi
i got this error while generating:
TypeError Cannot assign Illuminate\Support\Collection to property Vyuldashev\LaravelOpenApi\RouteInformation::$parameters of type array
Hi, I have just upgraded to v1.0.2 from v0 and I'm unable to generate the API doc when there is multiple responses in a single controller method. My code looks like this:
#[OpenApi\Operation(id: 'invitationAccept', tags: ['Invitation'])]
#[OpenApi\Parameters(factory: DefaultHeaderParameters::class)]
#[OpenApi\Response(factory: SuccessResponse::class, statusCode: 200)]
#[OpenApi\Response(factory: BadRequestResponse::class, statusCode: 400)]
public function accept(Invitation $invitation)
{
...
}
And I get this error when running the openapi:generate
command:
Attribute "Vyuldashev\LaravelOpenApi\Attributes\Response" must not be repeated
at vendor/vyuldashev/laravel-openapi/src/RouteInformation.php:84
80▕ $controllerAttributes = collect($reflectionClass->getAttributes())
81▕ ->map(fn(ReflectionAttribute $attribute) => $attribute->newInstance());
82▕
83▕ $actionAttributes = collect($reflectionMethod->getAttributes())
➜ 84▕ ->map(fn(ReflectionAttribute $attribute) => $attribute->newInstance());
85▕
86▕ $instance->domain = $route->domain();
87▕ $instance->method = $method;
88▕ $instance->uri = Str::start($route->uri(), '/');
+4 vendor frames
5 [internal]:0
Vyuldashev\LaravelOpenApi\Builders\PathsBuilder::Vyuldashev\LaravelOpenApi\Builders\{closure}()
+18 vendor frames
24 artisan:37
Illuminate\Foundation\Console\Kernel::handle()
My current project is on PHP 8.0, Laravel 8, laravel-openapi 1.0.2. I haven't tested this issue on a fresh laravel installation yet.
When I'm defining my schemas and they're referencing each other the generation hangs.
Consider the following example.
// UserSchema.php
return Schema::object('User')
->properties(
Schema::array('vehicles')->items(VehicleSchema::ref())->description('The related vehicles')->default(null),
)
// VehicleSchema.php
return Schema::object('Vehicle')
->properties(
Schema::object('user')->additionalProperties(UserSchema::ref())->description('The user relation')->default(null),
)
Both of these are implementing the Reusable interface.
When trying to install using composer require, I receive this error below:
ErrorException
Invalid argument supplied for foreach()
at vendor/vyuldashev/laravel-openapi/routes/api.php:8
4▕ use Illuminate\Support\Facades\Route;
5▕ use Vyuldashev\LaravelOpenApi\Http\OpenApiController;
6▕
7▕ Route::group(['as' => 'openapi.'], function () {
➜ 8▕ foreach (config('openapi.collections') as $name => $config) {
9▕ $uri = Arr::get($config, 'route.uri');
10▕
11▕ if (! $uri) {
12▕ continue;
+14 vendor frames
15 [internal]:0
Illuminate\Foundation\Application::Illuminate\Foundation\{closure}()
+5 vendor frames
21 artisan:37
Illuminate\Foundation\Console\Kernel::handle()
I also tried copying the openapi config file first, but still no luck.
PHP Version: 7.4
Laravel Version: 8.0
I'm posting an issue before starting a PR, but I'm willing to (help) implement this if this is not already possible.
I would like to define query parameters in the open api definition that we generate with this package. (https://stackoverflow.com/questions/63862351/openapi-how-to-describe-query-parameters)
If this is not yet possible, I assume that we need to define something on the schema for having the "in"?
If I try to set null
as an example of some field, it will be ignored.
Example:
Schema::string('foo')
->nullable()
->example(null),
It will look like this:
"foo": {
"type": "string",
"nullable": true
},
But expected:
"foo": {
"type": "string",
"nullable": true,
"example": null
},
It will be correctly rendered using SwaggerUI or Redoc.
At the moment, most generated requests for OpenApi are done via attributes.
However, the summary is still done via docblock reflection.
I propose to add a summary Attribute to replace docblock reflection.
#[OpenApi\Summary(string: "The summary of the request.")]
This will be used to populate the summary paramter of the request definition.
From my breif look through the code, this would allow the project to drop phpdocumentor/reflection-docblock
dependancy
When configuring global security it is not posible to use config:cache command as it breaks with the "Your configuration files are not serializable." exception.
At config/openapi.php:
'security' => [ GoldSpecDigital\ObjectOrientedOAS\Objects\SecurityRequirement::create()->securityScheme('oauth2'), ],
Exception:
I solved the issue using an array for the security specification, but had to override the GoldSpecDigital\ObjectOrientedOAS\OpenApi class.
Hello,
To ease my process I have created the start of a command to generate Parameters from a validation request. I think it could be interesting to perhaps add this feature? But, if someone need it or my example could bootstrap something, here is my code (with the possibility of overwrite in mind)
<?php
namespace App\Console\Commands;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Routing\Route;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Illuminate\Validation\ValidationRuleParser;
use Illuminate\Validation\Validator;
use Symfony\Component\Console\Input\InputOption;
class ParametersFactoryMakeCommand extends GeneratorCommand
{
protected $name = 'mm:make-parameters';
protected $description = 'Create a new Parameters factory class based on an existing request class';
protected $type = 'Parameters';
// @see https://laravel.com/docs/9.x/validation#available-validation-rules
protected array $mapRules = [
// @see https://swagger.io/docs/specification/data-models/data-types/
'Numeric' => ['schema.type' => 'number'],
'Integer' => ['schema.type' => 'integer'],
'String' => ['schema.type' => 'string'],
'Boolean' => ['schema.type' => 'boolean'],
'Array' => 'mapRuleArray',
// @see https://swagger.io/docs/specification/describing-parameters/
'Required' => ['required' => true, 'allowEmptyValue' => false],
'Present' => ['allowEmptyValue' => true],
'Prohibited' => ['allowEmptyValue' => true],
'Filled' => ['allowEmptyValue' => false],
'Nullable' => ['nullable' => true],
'In' => 'mapRuleIn',
];
protected function getOptions(): array
{
return [
['request', 'r', InputOption::VALUE_OPTIONAL, 'The request class parameters being generated for'],
['force', null, InputOption::VALUE_NONE, 'Create the class even if the factory already exists'],
['route', 'R', InputOption::VALUE_OPTIONAL, 'The route name associated to'],
['action', 'a', InputOption::VALUE_OPTIONAL, 'The route action associated to'],
];
}
protected function getStub(): string
{
return resource_path('/stubs/openAPI/parameters.stub');
}
protected function buildClass($name)
{
$output = parent::buildClass($name);
$fields = $this->extractFieldFromRequest($this->option('request'));
$fields += $this->extractFieldFromRouteName($this->option('route'));
$fields += $this->extractFieldFromRouteAction($this->option('action'));
$output = $this->buildClassFromFieldsInfos($output, $fields);
return $output;
}
protected function buildClassFromFieldsInfos(string $output, array $fields): string
{
$definition = [''];
$spacer = ' ';
foreach ($fields as $field)
{
$definition []= $spacer . $this->assembleField($this->renderField($field, $spacer));
}
return str_replace('DummyDefinition', implode(PHP_EOL, $definition), $output);
}
protected function getDefaultNamespace($rootNamespace): string
{
return $rootNamespace . '\OpenApi\Parameters';
}
protected function qualifyClass($name): string
{
$name = parent::qualifyClass($name);
if (Str::endsWith($name, 'Parameters'))
{
return $name;
}
return $name . 'Parameters';
}
protected function buildValidatorFromRequest(string $request): Validator
{
try
{
/** @var FormRequest $request */
$request = app($request);
}
catch (ValidationException $e)
{
return $e->validator;
}
// if build of validation doesn't generate any error, we try to build the validator
// see \Illuminate\Foundation\Http\FormRequest::getValidatorInstance
/** @var ValidationFactory $factory */
$factory = app(ValidationFactory::class);
if (method_exists($request, 'validator'))
{
$validator = app()->call([$request, 'validator'], compact('factory'));
}
else
{
$validator = $factory->make(
[],
app()->call([$request, 'rules']),
$request->messages(),
$request->attributes(),
);
}
return $validator;
}
private function extractRulesFromRequest(string $request): array
{
$validator = $this->buildValidatorFromRequest($request);
$rules = [];
if (method_exists($validator, 'getRules'))
{
$rules = call_user_func([$validator, 'getRules']);
}
return $rules;
}
protected function mapRule(string $field, string $attribute, string $rule, array $parameters, array $attributeRules): array
{
if (array_key_exists($rule, $this->mapRules))
{
if (is_array($this->mapRules[ $rule ]))
{
return $this->mapRules[$rule];
}
return $this->{$this->mapRules[ $rule ]}($field, $attribute, $rule, $parameters, $attributeRules);
}
return [];
}
protected function mapRuleIn(string $field, string $attribute, string $rule, array $parameters, array $attributeRules): array
{
return ['schema.enum' => $parameters];
}
protected function mapRuleArray(string $field, string $attribute, string $rule, array $parameters, array $attributeRules): array
{
// TODO type array or object => see $attributeRules
return ['schema.type' => 'array'];
}
protected function mapRulesToField(array $fields, string $field, string $attribute, array $attributeRules): array
{
foreach ($attributeRules as $rule)
{
[$rule, $parameters] = ValidationRuleParser::parse($rule);
if ($rule === '')
{
continue;
}
$ruleMapped = $this->mapRule($field, $attribute, $rule, $parameters, $attributeRules);
$fields[ $field ] = $ruleMapped + $fields[ $field ];
}
return $fields;
}
protected function renderField(array $field, string $spacer): array
{
$field = Arr::undot($field);
$output = [
$spacer . 'Parameter::query()',
$spacer . ' ->name(\'' . $field['name'] . '\')',
];
if (array_key_exists('required', $field))
{
$output []= $spacer . ' ->required(' . ($field['required'] ? 'true' : 'false') . ')';
}
if (array_key_exists('allowEmptyValue', $field))
{
$output []= $spacer . ' ->allowEmptyValue(' . ($field['allowEmptyValue'] ? 'true' : 'false') . ')';
}
if (array_key_exists('schema', $field))
{
$schema = $field['schema'];
$output []= $spacer . ' ->schema(';
$output = array_merge($output, $this->renderFieldSchema($schema, $spacer . ' '));
$output []= $spacer . ' )';
}
return $output;
}
private function assembleField(array $renderField): string
{
return '$parameters [] = ' . implode(PHP_EOL, $renderField) . ';';
}
protected function renderFieldSchema(array $schema, string $spacer): array
{
$output = [];
$output []= $spacer . 'Schema::' . $schema['type'] . '()';
if (array_key_exists('enum', $schema))
{
$output []= $spacer . ' ->enum(' . var_export($schema['enum'], true) . ')';
}
return $output;
}
protected function extractFieldFromRequest(?string $request): array
{
if (!$request)
{
return [];
}
if (!is_a($request, FormRequest::class, true))
{
throw new \InvalidArgumentException('Invalid request, it must be a FormRequest');
}
// extract rules from request validator
$rules = $this->extractRulesFromRequest($request);
// @see \Illuminate\Validation\Validator::passes
$fields = [];
foreach ($rules as $attribute => $attributeRules)
{
$field = explode('.', $attribute)[0];
$fields[ $field ] = $fields[ $field ] ?? ['name' => $field];
$fields = $this->mapRulesToField($fields, $field, $attribute, $attributeRules);
}
return $fields;
}
protected function extractFieldFromRouteName(?string $routeName): array
{
if (!$routeName)
{
return [];
}
$route = app('router')->getByName($routeName);
return $route ? $this->routeToFields($route) : [];
}
protected function extractFieldFromRouteAction(?string $action): array
{
if (!$action)
{
return [];
}
$router = app('router')->getRoutes();
$route = $router->getByAction($action) ?? $router->getByAction('App\\Http\\Controllers\\' . $action);
return $route ? $this->routeToFields($route) : [];
}
protected function routeToFields(Route $route): array
{
// $parameters = $route->parameterNames(); // don't give optional parameters
preg_match_all('/{(.*?)}/', $route->uri, $parameters);
$parameters = collect($parameters[1]);
if (count($parameters) > 0)
{
$parameters = $parameters->mapWithKeys(static fn ($parameter) => [Str::replaceLast('?', '', $parameter) => [
'name' => Str::replaceLast('?', '', $parameter),
'required' => ! Str::endsWith($parameter, '?'),
]]);
}
/** @var \ReflectionParameter $signatureParameter */
foreach ($route->signatureParameters() as $signatureParameter)
{
$parameter = $parameters[ $signatureParameter->getName() ] ?? [
'name' => $signatureParameter->getName(),
'required' => $signatureParameter->isOptional(),
];
if ($signatureParameter->hasType())
{
$type = ucFirst($signatureParameter->getType()->getName());
if (is_a($type, FormRequest::class, true))
{
$parameters = $parameters->union($this->extractFieldFromRequest($type));
$parameters->offsetUnset($signatureParameter->getName());
continue;
}
if (array_key_exists($type, $this->mapRules) && is_array($this->mapRules[$type]))
{
$parameter['type'] = $this->mapRules[$type];
}
}
if (!$parameter['required'])
{
$parameter['allowEmptyValue'] = $signatureParameter->isOptional() || $signatureParameter->allowsNull() || $signatureParameter->isDefaultValueAvailable();
}
$parameters->put($signatureParameter->getName(), $parameter);
}
return $parameters->toArray();
}
}
The following error is seen when generating the OpenAPI JSON from the command line (artisan openapi:generate
). This is for a bare controller with no editing beyond the Laravel boilerplate for artisan make:controller --api
; no #[attribute]
annotations were added to the controller, only a Route::resource()
entry in routes/api.php
.
ReflectionException
Method App\Http\Controllers\FooController::create() does not exist
at vendor/vyuldashev/laravel-openapi/src/RouteInformation.php:75
71▕ ]);
72▕ }
73▕
74▕ $reflectionClass = new ReflectionClass($controller);
➜ 75▕ $reflectionMethod = $reflectionClass->getMethod($action);
76▕
77▕ $docComment = $reflectionMethod->getDocComment();
78▕ $docBlock = $docComment ? DocBlockFactory::createInstance()->create($docComment) : null;
79▕
+4 vendor frames
5 [internal]:0
Vyuldashev\LaravelOpenApi\Builders\PathsBuilder::Vyuldashev\LaravelOpenApi\Builders\{closure}(Object(Illuminate\Routing\Route))
+19 vendor frames
25 artisan:37
Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
composer create-project laravel/laravel myproject
cd myproject
composer require 'php=^8.0'
composer require 'vyuldashev/laravel-openapi'
php artisan make:controller --api FooController
routes/api.php
file:Route::resource('/foo', App\Http\Controllers\FooController::class);
php artisan openapi:generate
.This results in the error I included above.
brew install [email protected]
){
"name": "laravel/laravel",
"type": "project",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"require": {
"php": "^8.0",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^9.11",
"laravel/sanctum": "^2.14.1",
"laravel/tinker": "^2.7",
"vyuldashev/laravel-openapi": "^1.5"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",
"laravel/sail": "^1.0.1",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^6.1",
"phpunit/phpunit": "^9.5.10",
"spatie/laravel-ignition": "^1.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}
Getting this error when running the command:
php artisan openapi:make-schema User -m Models\\User
My User table has a status
enum with values pending
, approved
, and a column of role
with admin
and customer
. Using MySQL 8 and Laravel 8.40.0
When using a resource controller, I see that the documentation shows only PUT
requests and no documentation for PATCH
requests.
Because of the subtle differences in PUT
(full resource data expected) vs. PATCH
(partial/changed resource data expected), I’d love to see the ability to specify which one—or both—should be available via the generated documentation.
One potential workaround is to register the API endpoints individually rather than using a resource controller.
Please I'd like to know what you used to achieve the documentation for this project. I'm looking for something similar?
Thank you
At the moment, it's impossible to use this for Controllers only meant for API's, because the package looks for the create
and edit
methods, as its shown here:
https://flareapp.io/share/VmeDeoy5#F48
It appears there's no way currently (or at least documented way I can find) to require more than one API Key header for the Security Requirements.
It looks like the OpenAPI documentation specify this is possible: https://swagger.io/docs/specification/authentication/api-keys/
components:
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-KEY
appId:
type: apiKey
in: header
name: X-APP-ID
security:
- apiKey: []
appId: [] # <-- no leading dash (-)
It would be good if this was supported to allow for requiring more than one security header.
I run php artisan openapi:generate but my schemas in app\OpenApi\Schemas not generated
Hello,
do you have plans to add support for OpenAPI 3.1? It introduced new webhook fields to specification which is a useful feature
When running composer require, it hits this error on api.php line 8. Can't install. Don't have this issue with composer require on any other packages I just tested.
In api.php line 8:
Invalid argument supplied for foreach()
Script @php artisan package:discover handling the post-autoload-dump event returned with error code 1
Installation failed, reverting ./composer.json and ./composer.lock to their original content.
Hi,
In the collection section of documentation there is not still documentation, it would be great to have it!
Do you have any examples of how to use request bodies and schemas?
I’d particularly like to use Schema::ref()
to refer to reusable schemas, but haven’t been able to get it to work.
Thanks!
The error message leaves something to be desired, as I don't know what about it is making it "invalid". Could you provide more feedback in the error message or details in the documentation?
I'm attempting an install on a new laravel 8 project
"require": {
"php": "^7.3|^8.0",
"fideloper/proxy": "^4.4",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.12",
"laravel/tinker": "^2.5"
},
and get the following error:
In Reader.php line 130:
cebe\openapi\Reader::readFromYamlFile(): Argument #1 ($fileName) must be of type string, null given, called in /var/www/html/vendor/mdwheele/laravel-openapi/src/OpenApiServiceProvider.
php on line 25
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.