Files
InvoiceShelf/app/Providers/ScrambleServiceProvider.php
Darko Gjorgjijoski 8d929ec09d feat(api): generate OpenAPI spec with Scramble for api-docs.invoiceshelf.com (#685)
Auto-generate an OpenAPI 3.1 spec from the v1 API's FormRequests and Resources
(no annotations) for publishing at api-docs.invoiceshelf.com as a static
Swagger UI site.

- config/scramble.php: scope to api/v1, version from version.md, clean
  placeholder server, export to public/openapi.json
- ScrambleServiceProvider: advertise Bearer (Sanctum) auth; add the required
  `company` tenancy header only to routes using the `company` middleware
- OpenApiDocumentationTest: assert spec shape, auth scheme, company-header gating
- .github/workflows/openapi.yml: export + commit spec on release, notify the
  api-docs site to rebuild
- public/openapi.json: generated seed spec (184 paths)
- dedoc/scramble added as a dev-only dependency

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 15:51:54 +02:00

65 lines
2.3 KiB
PHP

<?php
namespace App\Providers;
use Dedoc\Scramble\Scramble;
use Dedoc\Scramble\Support\Generator\OpenApi;
use Dedoc\Scramble\Support\Generator\Operation;
use Dedoc\Scramble\Support\Generator\Parameter;
use Dedoc\Scramble\Support\Generator\Schema;
use Dedoc\Scramble\Support\Generator\SecurityScheme;
use Dedoc\Scramble\Support\Generator\Types\StringType;
use Dedoc\Scramble\Support\RouteInfo;
use Illuminate\Support\ServiceProvider;
/**
* Configures Scramble's OpenAPI generation for the InvoiceShelf API.
*
* Scramble is a dev-only dependency used to generate the spec in CI/local (see
* `php artisan scramble:export`); it is absent in production (composer install
* --no-dev). Every reference to it is therefore guarded by class_exists().
*/
class ScrambleServiceProvider extends ServiceProvider
{
public function boot(): void
{
if (! class_exists(Scramble::class)) {
return;
}
Scramble::configure()
->withDocumentTransformers($this->documentTransformer(...))
->withOperationTransformers($this->operationTransformer(...));
}
/**
* Advertise Bearer (Sanctum personal access token) auth as the global
* security scheme. The customer-portal guard (auth:customer) is session-based
* and not part of the token API surface, so it is intentionally not exposed.
*/
protected function documentTransformer(OpenApi $openApi): void
{
$openApi->secure(SecurityScheme::http('bearer'));
}
/**
* Add the multi-tenancy `company` header to every company-scoped operation.
* CompanyMiddleware (alias `company`) resolves the active tenant from this
* header, so only routes that actually run it get the parameter — auth, ping,
* and installation endpoints stay clean.
*/
protected function operationTransformer(Operation $operation, RouteInfo $routeInfo): void
{
if (! in_array('company', $routeInfo->route->gatherMiddleware(), true)) {
return;
}
$operation->addParameters([
Parameter::make('company', 'header')
->description('ID of the company the request operates on (multi-tenancy).')
->setSchema(Schema::fromType(new StringType))
->required(true),
]);
}
}