What happened?
Use PHP Swagger API docs generator with https://github.com/DarkaOnLine/L5-Swagger and https://github.com/zircote/swagger-php to add these codeblocks to the stubs.
use OpenApi\Attributes as OA;
#[OA\Info(
title:"API Documentation",
version: "0.1",
contact: new OA\Contact(name: "", email: "")
)]
#[OA\Server(
description: "API Server",
url: "http://localhost:8000/api/"
)]
#[OA\SecurityScheme(
name: "bearerAuth",
securityScheme: "bearerAuth",
type: "http",
scheme: "bearer",
description: "Enter JWT Token",
in: "header"
)]
#[OA\PathParameter(
name: 'tenant',
parameter: 'tenant',
description: 'ID of the tenant',
schema: new OA\Schema(type: 'integer'),
required: TENANT_AWARENESS,
in: "path",
)]
Most of the parameters can be set as custom CONSTANTS when the plugin is configured via config variables in l5-swagger.php
define('TENANT_AWARENESS', config('api-service.tenancy.awareness');
or so...
check if tenancy for resource is enabled set constant per resource in for example the BrandApiService.php
#[OA\Tag(
name: "Brands",
description: "API Endpoints of Brands",
)]
class BrandApiService extends ApiService
{
public static function handlers(): array
{
if (
ApiService::isTenancyEnabled() &&
ApiService::tenancyAwareness() &&
static::getResource()::isScopedToTenant()
) {
define('TENANT_AWARENESS_BRANDS', true);
} else {
define('TENANT_AWARENESS_BRANDS', false);
}
$handlers = [
Handlers\CreateHandler::class,
Handlers\UpdateHandler::class,
Handlers\DeleteHandler::class,
Handlers\PaginationHandler::class,
Handlers\DetailHandler::class
];
foreach($handlers as $handler) {
if(app($handler)->isPublic()) {
define('RESOURCE_PUBLIC_BRANDS_' . Str::upper(app($handler)->getKebabClassName()), true);
} else {
define('RESOURCE_PUBLIC_BRANDS_' . Str::upper(app($handler)->getKebabClassName()), false);
}
}
return $handlers;
}
}
as an example for the different resource API Handlers.php (CreateHandler.php, UpdateHandler.php etc...) :
use OpenApi\Attributes as OA;
#[OA\Post(
path: "/admin/" . ((TENANT_AWARENESS_BRANDS) ? "{tenant}/" : "") . "brands",
operationId: "storeBrand",
tags: ["Brands"],
summary: "Store new Brand",
description: "Returns inserted brand data",
security: (!RESOURCE_PUBLIC_BRANDS_CREATE) ? [["bearerAuth" => []]]: null,
parameters: [
(TENANT_AWARENESS_BRANDS) ? new OA\Parameter(ref: "#/components/parameters/tenant") : null,
],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(ref: "#/components/schemas/BrandTransformer/properties/data/items")
),
responses: [
new OA\Response(response: 200, description: 'Operation succesful', content: new OA\JsonContent(type: "object", properties: [
new OA\Property(property: "message", type: "string", example: "Successfully Inserted Resource"),
new OA\Property(property: "data", type: "object", ref: "#/components/schemas/BrandTransformer/properties/data/items")
])),
new OA\Response(response: 400, description: 'Bad Request'),
new OA\Response(response: 401, description: 'Unauthenticated'),
new OA\Response(response: 403, description: 'Forbidden'),
new OA\Response(response: 404, description: 'Resource not Found'),
]
)]
and
#[OA\Get(
path: "/admin/" . ((TENANT_AWARENESS_BRANDS) ? "{tenant}/" : "") . "brands/{id}",
operationId: "getBrandDetail",
tags: ["Brands"],
summary: "Get detail of Brand",
description: "Returns detail of Brand",
security: (!RESOURCE_PUBLIC_BRANDS_DETAIL) ? [["bearerAuth" => []]]: null,
parameters: [
(TENANT_AWARENESS_BRANDS) ? new OA\Parameter(ref: "#/components/parameters/tenant") : null,
new OA\Parameter(
name: "id",
description: "Brand ID",
required: true,
in: "path",
schema: new OA\Schema(type: "integer"),
example: "", // OA\Examples(example="int", value="0", summary="An int value."),
),
new OA\Parameter(
name: "page[offset]",
description: "Pagination offset option",
required: false,
in: "query",
schema: new OA\Schema(type: "integer"),
example: "", // OA\Examples(example="int", value="0", summary="An int value."),
),
new OA\Parameter(
name: "page[limit]",
description: "Pagination limit option",
required: false,
in: "query",
schema: new OA\Schema(type: "integer"),
example: "", // OA\Examples(example="int", value="0", summary="An int value."),
),
new OA\Parameter(
name: "sort",
description: "Sorting",
required: false,
in: "query",
schema: new OA\Schema(type: "string"),
example: "", // @OA\Examples(example="string", value="-created,name", summary="A comma separated value"),
),
new OA\Parameter(
name: "include",
description: "Include Relationships",
required: false,
in: "query",
schema: new OA\Schema(type: "string"),
example: "", // @OA\Examples(example="string", value="order,user", summary="A comma separated value of relationships"),
),
],
responses: [
new OA\Response(response: 200, description: 'Operation succesful', content: new OA\JsonContent( type: "object", ref: "#/components/schemas/BrandTransformer/properties/data/items")),
new OA\Response(response: 400, description: 'Bad Request'),
new OA\Response(response: 401, description: 'Unauthenticated'),
new OA\Response(response: 403, description: 'Forbidden'),
new OA\Response(response: 404, description: 'Resource not Found'),
]
)]
and BrandTransformer.php
#[OA\Schema(
title: "BrandTransformer",
description: "Brands API Transformer",
xml: new OA\XML(name: "BrandTransformer"),
)]
and the transformer schema
#[OA\Property(
property: "data",
type: "array",
items: new OA\Items(
properties: [
new OA\Property(property: "id", type: "integer", title: "ID", description: "id of the brand", example: ""),
// add your own...
new OA\Property(property: "created_at", type: "string", title: "Created at", description: "Created at Datetime of Product", example: ""),
new OA\Property(property: "updated_at", type: "string", title: "Updated at", description: "Updated at Datetime of Product", example: ""),
]
),
)]
#[OA\Property(
property: "meta",
type: "object",
properties: [
new OA\Property(property: "current_page", type: "integer", title: "Current Page", description: "current page of brand", example: ""),
new OA\Property(property: "from", type: "integer", title: "", description: "", example: ""),
new OA\Property(property: "last_page", type: "integer", title: "", description: "", example: ""),
new OA\Property(property: "path", type: "string", title: "", description: "", example: ""),
new OA\Property(property: "per_page", type: "integer", title: "", description: "", example: ""),
new OA\Property(property: "to", type: "integer", title: "", description: "", example: ""),
new OA\Property(property: "total", type: "integer", title: "", description: "", example: ""),
new OA\Property(property: "", type: "integer", title: "", description: "", example: ""),
new OA\Property(
property: "links",
type: "array",
items: new OA\Items(properties: [
new OA\Property(property: "url", type: "string", description: "URL of Pagination"),
new OA\Property(property: "label", type: "string", description: "Label of Pagination"),
new OA\Property(property: "active", type: "bool", description: "Pagination is Active"),
])
)
]
)]
#[OA\Property(
property: "links",
type: "object",
properties: [
new OA\Property(property: "first", type: "string", description: "first page link URL of brand", example: ""),
new OA\Property(property: "last", type: "string", description: "last page link URL of brand", example: ""),
new OA\Property(property: "prev", type: "string", description: "prev page link URL of brand", example: ""),
new OA\Property(property: "next", type: "string", description: "next page link URL of brand", example: ""),
])
]
How to reproduce the bug
Implement OpenApi\Attributes;
Package Version
3.2
PHP Version
8.3
Laravel Version
10.0
Which operating systems does with happen with?
No response
Notes
This is just a start and code is not always cleaned up and i'm not quite sure everything is covered like response codes, panel prefix as CONST so it can be added in path: etc.... I could make time to add these features and do a PR if you prefer.