Giter Club home page Giter Club logo

cakephp-swagger-bake's Introduction

Swagger Bake

A delightfully tasty plugin for generating OpenAPI, Swagger and Redoc

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

Automatically generate OpenApi, Swagger, and Redoc documentation from your existing CakePHP code

  • Creates OpenApi paths and operations from your RESTful routes and controllers.
  • Creates OpenAPI Schema from your Entities and Tables.
  • Integrates with: Paginator, friendsofcake/search, Validator, and Bake.
  • Provides additional functionality through Attributes and Doc Blocks.

The current release is for CakePHP 5 and PHP 8.1, see previous releases for older versions of CakePHP and PHP.

Version Branch Cake Version PHP Version
3.* master ^5.0 ^8.1
2.* 2.x ^4.2 ^8.0
1.* 1.next ^4.0 ^7.2

Check out the demo applications for examples.

Demos Source
Swagger Bake v3 demo https://github.com/cnizzardini/cakephp-swagger-bake-demo
MixerAPI + Swagger Bake demo (v3) https://github.com/mixerapi/demo
v2 demo https://github.com/cnizzardini/cakephp-swagger-bake-demo/tree/2.next
v1 demo https://github.com/cnizzardini/cakephp-swagger-bake-demo/tree/1.next

Table of Contents

Installation

You can install via the composer dependency manager:

composer require cnizzardini/cakephp-swagger-bake

Next load the plugin via bin/cake plugin load SwaggerBake or if you prefer manually:

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

For standard applications that have not split their API into plugins, the automated setup is the easiest. Otherwise, skip to the manual setup.

Automated Setup

Use the swagger install command and then add a route.

bin/cake swagger install

Manual Setup

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

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

For more read sections on Multiple Instances of SwaggerBake and Extending Views and Controllers

Add Route

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

$builder->connect(
    '/api', # this will be the "homepage" for your Swagger or Redoc UI
    ['plugin' => 'SwaggerBake', 'controller' => 'Swagger', 'action' => 'index']
);

You can now browse to either /api for swagger or /api?doctype=redoc for redoc. Your OpenAPI JSON will exist at /api/swagger.json.

Getting Started

  • You can generate OpenAPI json from the command line at anytime by running the swagger bake command:
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.

  • You can also generate OpenAPI programmatically:

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

Routes

Your RESTful routes are used to build OpenAPI paths and operations.

Controllers

SwaggerBake will parse the DocBlocks on your controller actions for additional OpenAPI data.

/**
 * OpenAPI Operation Summary
 * 
 * This displays as the operations long description
 * 
 * @link https://book.cakephp.org/5/en/index.html External documentation
 * @deprecated Indicates the operation is deprecated
 * @throws \Cake\Http\Exception\BadRequestException Appears as 400 response with this description
 * @throws \Exception Appears as 500 response with this description
 */
public function index() {}

If you prefer, you may use the OpenApiOperation, OpenApiResponse attributes instead. These attributes take precedence over doc block parsing. Read below for a full list of attributes.

Models

OpenAPI schema is built from your Table and Entity classes and any validators you've defined in them. You may adjust the default schema using the OpenApiSchema and OpenApiSchemaProperty attributes.

Attributes

For additional functionality the following PHP8 Attributes may be used. These can be imported individually from the SwaggerBake\Lib\Attribute namespace. Read the Attributes docs for detailed examples. If you are using version 1 you will need to use annotations.

Attribute Usage Description
OpenApiDto Controller Action Builds OpenAPI query params and request bodies from Data Transfer Objects
OpenApiForm Controller Action Builds OpenAPI for application/x-www-form-urlencoded request bodies
OpenApiHeader Controller Action Create OpenAPI header parameters
OpenApiOperation Controller Action Modifies OpenAPI operation
OpenApiPaginator Controller Action Create OpenAPI query params from CakePHP Paginator Component
OpenApiPath Controller Modifies OpenAPI paths
OpenApiPathParam Controller Action Modify an existing OpenAPI path parameter
OpenApiQueryParam Controller Action Builds OpenAPI query param
OpenApiRequestBody Controller Action Modify OpenAPI request body
OpenApiResponse Controller Action Modify OpenAPI response
OpenApiSchema Entity Modifies OpenAPI schema
OpenApiSchemaProperty Entity or Class Modifies an OpenAPI schema property or defines OpenApiResponse schema
OpenApiSearch Controller Action Create OpenAPI query params from CakePHP Search plugin
OpenApiSecurity Controller Action Create/modify OpenAPI security

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.Path.created Dispatched each time an OpenAPI Path 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 Response Samples

By default, SwaggerBake uses '#/components/schemas/Exception' as your OpenAPI documentations Exception schema. See the default swagger.yml and exceptionSchema in swagger_bake.php for more info. You can further customize with attributes and @throws.

OpenApiResponse

Using the ref or schema properties of OpenApiResponse.

Using the @throws tag and OpenApiExceptionSchemaInterface

Implement SwaggerBake\Lib\OpenApiExceptionSchemaInterface on your exception class, then document the exception with a @throws tag in your controller action's doc block.

/**
 * @throws \App\Exception\MyException
 */
public function add(){}

Example exception class:

class MyException implements OpenApiExceptionSchemaInterface 
{
    public static function getExceptionCode(): string
    {
        return '400';
    }

    public static function getExceptionDescription(): ?string
    {
        return 'An optional description'; // returning null omits the response description
    }

    public static function getExceptionSchema(): Schema|string
    {
        return (new \SwaggerBake\Lib\OpenApi\Schema())  
            ->setTitle('MyException')
            ->setProperties([
                (new SchemaProperty())->setType('string')->setName('code')->setExample('400'),
                (new SchemaProperty())->setType('string')->setName('message')->setExample('error'),
                (new SchemaProperty())->setType('string')->setName('wherever')->setExample('whatever you want')
            ]);
    }
}

Extending Views and Controllers

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

Using Your Own SwaggerUI

You may use your own swagger or redoc 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).

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         
    }

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

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.

Missing routes

Make sure yours route are properly defined in config/routes.php per the CakePHP RESTful routing documentation.

Missing request or response samples

Sample schema is determined using CakePHP naming conventions. Does your controller name match your model names? For customizing response schema see OpenApiResponse.

Missing request schema

Sample schema is determined using CakePHP naming conventions. Does your controller name match your model names? For customizing request schema see OpenApiRequestBody.

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.

HTTP DELETE issues with Swagger UI

Swagger UI sends HTTP DELETE without an accept header. If the record does not exist, an exception is generated. This results in an HTML response being generated which can be quite large and cause the UI to be slow to render. To get around this you can force an accept value on the header using the CakePHP middleware:

# src/Application.php

public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
	$middlewareQueue
	    ->add(function(ServerRequestInterface $request, RequestHandlerInterface $handler){
	        $accept = $request->getHeader('accept');
	        if ($request->getMethod() === 'DELETE' && reset($accept) === '*/*') {
	            $request = $request->withHeader('accept', 'application/json');
	        }

	        return $handler->handle($request);
	    });

	// other middleware...
	
	return $middlewareQueue;
}

Read more about CakePHP middleware in the official documentation.

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

Check out the extensions documentation to add functionality to this project.

Tests + Analysis

PHPUnit Test Suite:

composer test

PHPUnit, PHPCS, PHPSTAN, and PHPMD:

composer analyze

GrumPHP can be used to run tests and static analyzers in a pre-commit hook.

composer grumphp-init

I've set grumphp to be installed globally: https://github.com/phpro/grumphp/blob/master/doc/installation/global.md

cakephp-swagger-bake's People

Contributors

adrian-tiberius avatar archangeldesign avatar bmudda avatar cnizzardini avatar dogmatic69 avatar jamisonbryant avatar julyanof avatar lpj145 avatar nomahideki avatar segy avatar theglenn88 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

cakephp-swagger-bake's Issues

Path spec

Path properties to be added:

  • ref ($ref)
  • summary
  • description

@SwagPath annotation properties:

  • ref ($ref)
  • summary
  • description

Putting it together:

https://docs.google.com/spreadsheets/d/e/2PACX-1vRTWE7nsTouFdHZsG6OKlZ-1lHeJGI0wqNlRVEgiG4eCFY0dMxkBLaw313mU_a73U7emoRdFcGPUq94/pubhtml
https://swagger.io/specification/

Refactor how controllers are instantiated

SwaggerBack needs to instantiate controllers for doctrine/annotations to parse @Swag annotations and phpdocumentor/reflection-docblock to parse PHP doc blocks.

To do this, SwaggerBake iterates over the namespaces.controllers configuration array until it can find the controller. It does this in a few different places. This kind of hacky.

It should be possible to use the data in RouteDecorator and the configured route prefix for routes.php to instantiate the class. Scenarios to account for are controllers in:

  • App\Controller
  • Controllers that are part of Userland plugins
  • Controllers that are child namespaces of App\Controller (e.g. namespace App\Controller\Admin)
  • Child namespace in Userland plugins (getting fancy now)

Unit tests don't exist for these scenarios, they will need to be built first before a refactor can begin.

  • App\Controller namespace
  • App\Controller child namespace
  • Plugin\Controller namespace
  • Plugin\Controller child namespace

Reference an entity from controller

Schema is only referenced by paths if the path is a core rest action: index, view, create, edit, and delete. Create a new annotation to force a reference in the response schema.

@SwagResponseSchema(refEntity="string")

See also #13

Unit tests for console commands

Console commands don't have any unit tests. Refactor of testing and/or commands may be needed to make them testable.

  • BakeCommand
  • InstallCommand
  • ModelCommand
  • RouteCommand

Create demo project

Public demo project available on github and on a public web server showing usage.

Add ability to hide/show an entity

Include an entity via an annotation such as @SwagEntity, example:

/**
 * Employee Entity
 *
 * @SwagEntity(isVisibile=bool)
 */
class Employee extends Entity
{
...

This will allow a user to:

  • Hide schemas from displaying using isVisible=false
  • Display schemas that are not tied to routes using isVisible=true

XML example cannot be generated; root element name is undefined

Describe the bug
When using XML request or response the following error is generated in Swagger UI:

XML example cannot be generated; root element name is undefined

See swagger-api/swagger-ui#4650

To Reproduce
Set swagger_bake.php config to use XML and reload swagger page. Then go to any post/patch or response.

        'requestAccepts' => ['application/xml'],
        'responseContentTypes' => ['application/xml'],

Expected behavior

Renders example without error

Create response codes from @throws

Response type codes of >= 400 and >= 500 can be created by looking at thrown exceptions.

Get 400 level exception types from Cake Exception classes.

Default to 500 if exception type is unknown or generic.

Schema spec

Schema properties to be added:

  • title
  • not

@SwagEntity annotation properties:

  • title
  • allOf
  • oneOf
  • anyOf
  • not

Putting it together:

  • Set new Schema properties from @SwagEntity annotations in SchemaFactory
  • New unit tests in SwagEntityTest will need annotations added to the test_app. @cnizzardini can assist with this if it proves difficult.

Related

Parameter spec

Parameter properties to be added:

  • deprecated
  • style
  • explode
  • allowReserved
  • example
  • format

@AbstractParameter annotation properties:

  • deprecated
  • allowEmptyValue
  • style
  • explode
  • allowReserved
  • example
  • format

Putting it together:

AbstractParameter is extended by @SwagQuery, @SwagForm, and @SwagHeader. These annotations are used by their respective classes in calls stemming from OperationFromRouteFactory

Related

Setup CI pipeline

  • Setup CI pipeline using one of the Saas providers: TravisCI, CircleCI, etc...

Support descriptions on @throw tags

Currently @throw only displays the exception classes short name in swagger ui. For example:

    /**
     * Edit method
     *
     * @param string|null $id Employee id.
     * @return \Cake\Http\Response|null|void HTTP 200 on successful edit
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */

Would only display as a 404 with "RecordNotFoundException" as the description. Add in the "When record not found" text to swagger ui.

Add ability to ignore a controller

Potential solution: Class level annotation on controllers such as @SwagIgnore or @SwagPath

/**
 * @SwagIgnore
 * @SwagPath(isVsible=false)
 */
class FooController
{
...

Support Data Transfer Objects

Read a Data Transfer Object (DTO) and optionally set as schema, response schema and/or request schema. To handle as many DTO libraries as possible consider using an Interface. For example:

interface SwaggerBakeDtoInterface
{
    public function getSwaggerReadableArray() : array;
}

class MyDto implements SwaggerBakeDtoInterface
{
    public function getSwaggerReadableArray() : array
    {
        return [
            'fieldName' => 'string',
        ];
    }
}

Need to consider if this will support deep nesting complex data types.

Alternatively. We could accept a DTO class as an annotation argument and parse that for Doc Block comments:

/**
 * @Swag\DataTransferObject(class="\App\Dto\MyDto")
 */
public function index() {}
/**@var string $my_var description of this attribute*/
private $my_var

SwagRequestBodyContent not working

Describe the bug
Argument 1 passed to SwaggerBake\Lib\OpenApi\Content::setSchema() must be an instance of SwaggerBake\Lib\OpenApi\Schema, string given, called in /var/www/personal/cakephp-swagger-bake/src/Lib/Annotation/SwagRequestBodyContentHandler.php on line 1

To Reproduce

Add the following to an HTTP POST method:

@Swag\SwagRequestBody(description="my description", required=true, ignoreCakeSchema=true)
@Swag\SwagRequestBodyContent(refEntity="#/components/schemas/Actor", mimeType="application/json")

Expected behavior
Expected JSON schema to be accepted

Feature Request: Merge Swagger.yml and PHP doc block

Right now, the definitions in config/swagger.yml override the ones in PHP doc block. It will be great to merge those so that documentation based detail can remain in doc block and overrides can be defined in the yml file.

Invalid data types per editor.swagger.io

Describe the bug
Swagger does not support decimal, float, uuid, or text. These need to be converted.

To Reproduce

  1. Generate schema using the cakephp-sakila-plugin
  2. Import JSON into editor.swagger.io
  3. Errors encountered

Expected behavior
No errors.

Schema Property spec

Schema Property properties to be added:

  • title
  • default
  • nullable
  • deprecated
  • multipleOf
  • maximum
  • exclusiveMaximum
  • minimum
  • exclusiveMinimum
  • maxLength
  • minLength
  • pattern
  • maxItems
  • minItems
  • uniqueItems
  • maxProperties
  • minProperties
  • enum

@SwagEntityAttribute annotation properties:

Cake Schema:

  • format

Cake Validator

  • nullable
  • enum (inList)
  • minimum (greaterThanOrEqual)
  • maximum (lessThanOrEqual)
  • maxLength (maxLength)
  • minLength (minLength)
  • pattern (regex)
  • exclusiveMaximum (lessThan)
  • exclusiveMinimum (greaterThan)
  • maxItems (hasAtMost)
  • minItems (hasAtLeast)

Unit Tests:

  • SchemaPropertyValidation
  • SwagEntityAttribute

Misc:

Custom schema type property not being converted properly

Describe the bug
components > schemas > NAME > type is not being set properly. object becomes 1.

To Reproduce
components:
schemas:
Exception:
type: object
properties:
code:
type: integer
example: 500
url:
type: string
example: /url/path
message:
type: string
example: Internal Error

Expected behavior
type object should remain as object.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.