/cakephp-swagger-bake

A CakePHP Swagger Generator. Automatically generate OpenAPI documentation from your code. No annotations required.

Primary LanguagePHPMIT LicenseMIT

SwaggerBake plugin for CakePHP4

Latest Version on Packagist Build Coverage Status License: MIT MixerApi CakePHP Minimum PHP Version OpenAPI

A delightfully tasty tool for generating Swagger documentation with OpenApi 3.0.0 schema. This plugin automatically builds your Swagger UI and ReDoc from your existing cake models and routes.

Demo Site | Demo Code | Screenshot

Table of Contents

Installation

SwaggerBake requires CakePHP4 and a few dependencies that will be automatically installed via composer.

composer require cnizzardini/cakephp-swagger-bake

Run bin/cake plugin load SwaggerBake or manually load the plugin:

# src/Application.php
public function bootstrap(): void
{
    // other logic...
    $this->addPlugin('SwaggerBake');
}

Setup

For standard applications that have not split their API into plugins, the automated setup should work. Otherwise use the manual setup.

Automated Setup

Run bin/cake swagger install and then add a route.

Manual Setup

  • Create a base swagger.yml file in config\swagger.yml. An example file is provided here.

  • Create a config/swagger_bake.php file. See the example file here for further explanation. Then just add a route.

Add a route to SwaggerUI

Create a route for the SwaggerUI page in config/routes.php, example:

$builder->connect('/my-swagger-ui', ['controller' => 'Swagger', 'action' => 'index', 'plugin' => 'SwaggerBake']);

Getting Started

  • You can generate OpenAPI json from the command line at anytime with bin/cake swagger bake.

  • If Hot Reload is enabled (see config) OpenAPI will be generated each time you browse to SwaggerUI (or Redoc) in your web browser.

  • Checkout the debug commands for troubleshooting and the bake theme for generating RESTful controllers.

Automatic Documentation

I built this library to reduce the need for annotations to build documentation. SwaggerBake will automatically build the following from your existing routes and models without additional effort:

  • Paths
    • Resource (route)
  • Operations
    • Summary and description
    • GET, POST, PATCH, DELETE
    • Form fields and JSON using your Cake models
    • Responses
    • Sub resources
    • Security/Authentication
  • Schema

See details for how CakePHP conventions are interpreted into OpenAPI 3.0 schema.

SwaggerBake works with your existing YML definitions and will not overwrite anything.

Doc Blocks

SwaggerBake will parse your DocBlocks for information. The first line reads as the Operation Summary and the second as the Operation Description, @see, @deprecated, and @throws are also supported. Throw tags use the Exception classes HTTP status code. For instance, a MethodNotAllowedException displays as a 405 response in Swagger UI, while a standard PHP Exception displays as a 500 code. You must use the FQN for exceptions.

/**
 * Swagger Operation Summary
 * 
 * This displays as the operations long description
 * 
 * @see https://book.cakephp.org/4/en/index.html The link and this description appear in Swagger
 * @deprecated
 * @throws \Cake\Http\Exception\BadRequestException An optional bad request description here
 * @throws \Exception
 */
public function index() {}

For Entities, the description from @property is supported:

/**
 * City Entity
 *
 * @property string $name Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
 * incididunt ut labore et dolore magna aliqua. 
 *
 * - some
 * - bullets
 *
 * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
 * magna aliqua.
 */

Annotations

SwaggerBake provides some optional Annotations for enhanced functionality. These can be imported individually from SwaggerBake\Lib\Annotation or set to an alias such as Swag: use SwaggerBake\Lib\Annotation as Swag. Read the Annotations docs for detailed examples.

Annotation Description
@SwagPaginator Supports CakePHP Paginator Component
@SwagSearch Supports Friends of Cake Search
@SwagQuery Adds query parameters
@SwagForm Adds form parameters
@SwagDto Adds Data Transfer Objects (DTOs)
@SwagHeader Adds header parameters
@SwagPathParameter Modifies path parameters
@SwagSecurity Adds/modifies authentication. Read below
@SwagOperation Modifies OpenAPI operations (can be used to hide operations)
@SwagRequestBody Describes OpenAPI request body
@SwagRequestBodyContent Describes OpenAPI request body content
@SwagResponseSchema Describes OpenAPI response schema
@SwagPath Describes OpenAPI paths (can be used to hide paths)
@SwagEntity Describes OpenAPI Entity (can be used to hide entity schemas)
@SwagEntityAttribute Add/modify OpenAPI schema properties

Event System

SwaggerBake comes with an event system to allow for further control over your OpenAPI schema.

Event Description
SwaggerBake.Operation.created Dispatched each time an OpenAPI Path > Operation is created
SwaggerBake.Schema.created Dispatched each time an OpenAPI Schema is created
SwaggerBake.initialize Dispatched during initialization phase on SwaggerBake
SwaggerBake.beforeRender Dispatched before SwaggerBake outputs OpenAPI JSON

Customizing Exception Responses

By default, SwaggerBake uses components > schemas > Exception as your Swagger documentations Exception schema. See the default swagger.yml and exceptionSchema in swagger_bake.php for more info.

You can provide custom schemas for exceptions by adding schema to your YAML at #/x-swagger-bake/components/schemas/app-exceptions and referencing the FQN of the exception with x-exception-fqn, example:

x-swagger-bake:
  components:
    schemas:
      app-exceptions:
        ValidationException:
          x-exception-fqn: '\MixerApi\ExceptionRender\ValidationException'
          type: object
          properties:
            exception:
              type: string
              example: ValidationException
            message:
              type: string
              example: Error saving resource `Schema`
            url:
              type: string
              example: /url/path
            code:
              type: integer
              example: 500
            violations:
              type: array
              items:
                $ref: '#/x-swagger-bake/components/schemas/app-exceptions/Violation'

Extending Views and Controllers

It's possible to write extensions for SwaggerBake. Read the extensions documentation. There are several other options to extend functionality documented below:

Using Your Own SwaggerUI

You may use your own swagger install in lieu of the version that comes with SwaggerBake. Simply don't add a custom route as indicated in the installation steps. In this case just reference the generated swagger.json within your userland Swagger UI install.

Using Your Own Controller

You might want to perform some additional logic (checking for authentication) before rendering the built-in Swagger UI. This is easy to do. Just create your own route and controller, then reference the built-in layout and template:

// config/routes.php
$builder->connect('/my-swagger-docs', ['controller' => 'MySwagger', 'action' => 'index']);

To get started, copy SwaggerController into your project.

Using Your Own Layout and Templates

You will need to use your own controller (see above). From there you can copy the layouts and templates into your project and inform your controller action to use them instead. Checkout out the CakePHP documentation on Views for specifics. This can be useful if you'd like to add additional functionality to SwaggerUI (or Redoc) using their APIs or if your project is not installed in your web servers document root (i.e. a sub-folder).

Generating OpenAPI

There a three options for generating swagger.json:

  1. Call swagger bake which can be included as part of your build process.

  2. Enable the hotReload option in config/swagger_bake.php (recommended for local development only).

  3. Call SwaggerBake programmatically:

$swagger = (new \SwaggerBake\Lib\Factory\SwaggerFactory())->create();
$swagger->getArray(); # returns swagger array
$swagger->toString(); # returns swagger json
$swagger->writeFile('/full/path/to/your/swagger.json'); # writes swagger.json

Multiple Instances of Swagger Bake

If your application has multiple APIs that are split into plugins you can generate unique OpenAPI schema, Swagger UI, and Redoc for each plugin. Setup a new swagger_bake.php and swagger.yaml in plugins/OtherApi/config. These configurations should point to your plugins paths and namespaces. Next, create a custom SwaggerController and load the configuration within initialize():

    public function initialize(): void
    {
        parent::initialize();
        Configure::load('OtherApi.swagger_bake', 'default', false); // note: `false` for the third argument is important
        $this->loadComponent('SwaggerBake.SwaggerUi');
    }

When running bin/cake swagger bake you will need to specify your plugins swagger_bake config:

bin/cake swagger bake --config OtherApi.swagger_bake

Debug Commands

In addition to swagger bake these console helpers provide insight into how your Swagger documentation is generated.

swagger routes

Displays a list of routes that can be viewed in Swagger.

bin/cake swagger routes

swagger models

Displays a list of models that can be viewed in Swagger.

bin/cake swagger models

Bake Theme

SwaggerBake comes with Bake templates for scaffolding RESTful controllers compatible with SwaggerBake and OpenAPI 3.0 schema. Using the bake theme is completely optional, but will save you some time since the default bake theme is not specifically designed for RESTful APIs.

bin/cake bake controller {Name} --theme SwaggerBake

Details

OpenAPI Schema Support Roadmap

  • Swagger uses your existing swagger.yml as a base for adding additional paths and schema.
  • Generates JSON based on the OpenAPI 3 specification. I am still working on implementing the full spec.
  • All Schemas and Paths generated must have the following in your CakePHP Application:
    • App\Model\Entity class (for schemas only)
    • App\Controller class
    • Must be a valid route
    • Three versions of schema will be created:
      • Default with all properties #/components/schemas/Entity
      • Writeable properties #/x-swagger-bake/components/schemas/Entity-Write
      • Readable properties #/x-swagger-bake/components/schemas/Entity-Read
  • Entity Attributes:
    • Reads descriptions from @property doc block tags.
    • Hidden attributes will not be visible.
    • Primary Keys will be set to read only by default.
    • DateTime fields named created and modified are automatically set to read only per Cake convention.
  • CRUD Responses
    • Index, Edit, Add, and View methods default to an HTTP 200 with the Controllers related Cake Entity schema.
    • Delete defaults to HTTP 204 (no content).
  • Table Validators:
    • Reads in Validator rules such as requirePresence, minLength, maxLength, basic math comparison operators, regex, inList, hasAtLeast, and hasAtMost.
  • Security Scheme
    • Leverages the CakePHP AuthenticationComponent
    • Will automatically set security on operations if a single securityScheme is defined in your swagger.yaml. If more than one security schema exists you will need to use @SwagSecurity.
    • @SwagSecurity takes precedence.
  • SwaggerBake supports the following formats: application/json, application/x-www-form-urlencoded, application/xml, application/hal+json, and application/ld+json

SwaggerBake does not document schema associations. If your application includes associations on things like GET requests, you can easily add them into your swagger documentation through the OpenAPI allOf property. Since SwaggerBake works in conjunction with OpenAPI YAML you can easily add a new schema with this association. Below is an example of extending an existing City schema to include a Country association.

# in your swagger.yml
components:
  schemas:
    CityExtended:
      description: 'City with extended information including Country'
      type: object
      allOf:
        - $ref: '#/components/schemas/City'
        - type: object
          properties:
            country:
              $ref: '#/components/schemas/Country'

Then in your controller action you'd specify the Schema:

/**
 * View method
 * @Swag\SwagResponseSchema(refEntity="#/components/schemas/CityExtended")
 */
public function view($id)
{
    $this->request->allowMethod('get');
    $city = $this->Cities->get($id, ['contain' => ['Countries']]);
    $this->set(compact('cities'));
    $this->viewBuilder()->setOption('serialize', 'cities');
}

The demo application includes this and many other examples of usage. Read more about oneOf, anyOf, allOf, and not in the OpenAPI 3 documentation.

Supported Versions

This is built for CakePHP 4.x only. A cake-3.8 option is available, but not supported.

Version Cake Version Supported Unit Tests Notes
1.* 4.* Yes Yes Currently supported
cake-3.8 3.8.* No Yes See branch cake-3.8. Completely untested and unsupported

Common Issues

Swagger UI

No API definition provided.

Verify that swagger.json exists.

SwaggerBakeRunTimeExceptions

Unable to create swagger file. Try creating an empty file first or checking permissions

Create the swagger.json manually matching the path in your config/swagger_bake.php file.

Output file is not writable

Change permissions on your swagger.json file, 764 should do.

Controller not found

Make sure a controller actually exists for the route resource.

Other Issues

Missing actions (missing paths) in Swagger

By default Cake RESTful resources will only create routes for index, view, add, edit and delete. You can add and remove paths using CakePHPs route resource functionality. Read the Cake Routing documentation which describes in detail how to add, remove, modify, and alter routes.

Missing CSRF token body

Either disable CSRF protection on your main route in config/routes.php or enable CSRF protection in Swagger UI. The library does not currently support adding this in for you.

My route isn't displaying in Swagger UI

Make sure the route is properly defined in your config/routes.php file.

Reporting Issues

This is a new library so please take some steps before reporting issues. You can copy & paste the JSON SwaggerBake outputs into https://editor.swagger.io/ which will automatically convert the JSON into YML and display potential schema issues.

Please included the following in your issues a long with a brief description:

  • Steps to Reproduce
  • Actual Outcome
  • Expected Outcome

Feature requests are welcomed.

Contribute

Send pull requests to help improve this library. You can include SwaggerBake in your primary Cake project as a local source to make developing easier:

  • Make a fork of this repository and clone it to your localhost

  • Remove cnizzardini\cakephp-swagger-bake from your composer.json

  • Add a paths repository to your composer.json

"minimum-stability": "dev",
"repositories": [
    {
        "type": "path",
        "url": "/absolute/local-path-to/cakephp-swagger-bake",
        "options": {
          "symlink": true
        }
    }
]
  • Run composer require cnizzardini/cakephp-swagger-bake @dev

Undo these steps when you're done. Read the full composer documentation on loading from path here: https://getcomposer.org/doc/05-repositories.md#path

Tests + Analysis

# unit tests only
composer test

# unit tests and static analysis
composer analyze