diff --git a/.gitignore b/.gitignore index 335b8b06..aa07bc91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,17 @@ .DS_Store composer.lock +*.composer.lock .php_cs.cache /vendor/ -/public +public/ +resources/docs/ +resources/views/apidoc/ +tests/public/ .idea/ coverage.xml results.xml docs/_build docs/make.bat +tests/public/docs/ +tests/resources/** +.phpunit.result.cache diff --git a/.styleci.yml b/.styleci.yml index 5651629b..5d4095a3 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,9 +1,2 @@ -preset: laravel +preset: psr12 -enabled: - - phpdoc_order - - phpdoc_separation - - unalign_double_arrow - -disabled: - - short_list_syntax \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index e6780ada..23cd9442 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,40 @@ +dist: bionic language: php -php: - - 7.0.0 - - 7.1.3 - - 7.2 - env: - matrix: - - PREFER_LOWEST="--prefer-lowest" - - PREFER_LOWEST="" + global: + - SETUP=stable -before_script: - - travis_retry composer self-update - - travis_retry composer update --no-interaction --prefer-dist $PREFER_LOWEST +matrix: + fast_finish: true + include: + - php: 7.4 + env: COMPOSER=dingo.composer.json + name: "With Dingo router" + - php: 7.4 + env: SETUP=lint + name: "Lint code" + - php: 7.2 + - php: 7.2 + env: SETUP=lowest + - php: 7.3 + - php: 7.3 + env: SETUP=lowest + - php: 7.4 + - php: 7.4 + env: SETUP=lowest -script: - - composer test-ci +cache: + directories: + - $HOME/.composer/cache before_install: - - pip install --user codecov + - travis_retry composer self-update + +install: + - if [[ $SETUP = 'stable' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-stable --no-suggest; fi + - if [[ $SETUP = 'lowest' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-lowest --prefer-stable --no-suggest; fi + - if [[ $SETUP = 'lint' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-stable --no-suggest; travis_retry composer lint; fi -after_success: - - codecov +script: + - if [[ $SETUP = 'lint' ]]; then exit 0; fi; composer test-ci; diff --git a/CHANGELOG.md b/CHANGELOG.md index 4511fbbe..aeeb3851 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,187 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +## 4.8.0 - Saturday, 2 May 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.7.0...4.8.0)) +### Added +- Support @hideFromAPIDocumentation on controllers. (https://github.com/mpociot/laravel-apidoc-generator/pull/745) +- Allow strategies to return null. (https://github.com/mpociot/laravel-apidoc-generator/pull/739) + +## 4.7.0 - Sunday, 12 April 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.6.0...4.7.0)) +### Added +- Support for Laravel Vapor. (https://github.com/mpociot/laravel-apidoc-generator/pull/729) +- Allow customization of static output path. (https://github.com/mpociot/laravel-apidoc-generator/pull/730) + +## 4.6.0 - Wednesday, 8 April 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.5.1...4.6.0)) +### Added +- Allow `@authenticated` to be set at controller level. (https://github.com/mpociot/laravel-apidoc-generator/pull/726) + +## 4.5.1 - Saturday, 4 April 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.5.0...4.5.1)) +### Fixed +- Fix version constraint...again. (https://github.com/mpociot/laravel-apidoc-generator/pull/725) + +## 4.5.0 - Tuesday, 31 March 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.4.3...4.5.0)) +### Fixed +- Fix version constraints preventing installation on some Laravel 7 installations. + +## 4.4.3 - Thursday, 26 March 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.4.2...4.4.3)) +### Fixed +- Fixed link to Postman connection in docs when Laravel autoload is used (https://github.com/mpociot/laravel-apidoc-generator/pull/714) + +## 4.4.2 - Sunday, 21 March 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.4.1...4.4.2)) +### Fixed +- Fixed double json encode when fetching collection in Laravel type docs (https://github.com/mpociot/laravel-apidoc-generator/pull/713) + + +## 4.4.1 - Wednesday, 11 March 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.4.0...4.4.1)) +### Added +- Support for body params as array (https://github.com/mpociot/laravel-apidoc-generator/pull/710) + +## 4.4.0 - Saturday, 7 March 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.3.1...4.4.0)) +### Fixed +- Array query params can now be used and render properly (https://github.com/mpociot/laravel-apidoc-generator/pull/700) + +## 4.3.1 - Friday, 6 March 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.3.0...4.3.1)) +### Changed +- Updated Documentarian dependency for Laravel v7 (https://github.com/mpociot/laravel-apidoc-generator/pull/699) + +## 4.3.0 - Saturday, 22 February 2020 ([compare to previous](https://github.com/mpociot/laravel-apidoc-generator/compare/4.2.4...4.3.0)) +### Changed +- Updated nunomaduro/collision to include v4 (https://github.com/mpociot/laravel-apidoc-generator/pull/699) + +### Fixed +- Use correct protocol for Postman collcetion URL (https://github.com/mpociot/laravel-apidoc-generator/pull/697) + +## [4.2.4] - Saturday, 15 February 2020 +### Fixed +- Shim URL::formatRoot() on Lumen (https://github.com/mpociot/laravel-apidoc-generator/pull/688) + +## [4.2.3] - Tuesday, 4 February 2020 +### Changed +- Made "Skipping route" message more descriptive (https://github.com/mpociot/laravel-apidoc-generator/commit/6f61469a9fa8be30e7812cf622a7832163a08bb8) + +## [4.2.2] - Tuesday, 21 January 2020 +### Fixed +- Set a default value for the routematcher when fetching from config (https://github.com/mpociot/laravel-apidoc-generator/pull/677) + +## [4.2.1] - Monday, 20 January 2020 +### Fixed +- Fixed autogenerated docs endpoint address for Postman collection (https://github.com/mpociot/laravel-apidoc-generator/pull/673) + +## [4.2.0] - Sunday, 19 January 2020 +### Added +- New Postman collection generation features (https://github.com/mpociot/laravel-apidoc-generator/pull/666): + - Properly handle url parameters using the `:param` syntax (opposed to the Laravel-esque `{param}` syntax) + - Allow configuring the auth section of Postman collection config + - Add some documentation that was available but not exported in the Postman collection (for URL params and query params) + +### Changed +- The package can now create a documentation endpoint automatically for `laravel`-type routes. This also allows users to install the package on dev-only enviornments but have their routes available in others, without writing custom routing code. (https://github.com/mpociot/laravel-apidoc-generator/pull/659) + +### Fixed +- Error when installing due to DI not working properly on constructor (https://github.com/mpociot/laravel-apidoc-generator/pull/672) + +## [4.1.0] - Monday, 6 January 2019 +### Added +- RouteMatcher to use can now be specified by user (https://github.com/mpociot/laravel-apidoc-generator/pull/657) + +### Fixed +- Also copy custom logo for non-static docs (https://github.com/mpociot/laravel-apidoc-generator/commit/720f9c9e9b2443bcfb474b959febaf6cf5c3f004) + +## [4.0.2] - Monday, 25 November 2019 +### Fixed +- Fixed missing body parameters in response calls (https://github.com/mpociot/laravel-apidoc-generator/commit/5d9371c14391485630941c718d7f168afd540126) +- Add slashes to header values in bash templates to escape special chars (https://github.com/mpociot/laravel-apidoc-generator/commit/e693d746b1c1daf342c28e53daa8f7b34ce9da2b) +- Fixed iteration over null bug - set responses to empty array (https://github.com/mpociot/laravel-apidoc-generator/commit/a24b1e14b17ade8fb4aa1534448904e1075b004c) + +## [4.0.1] - Monday, 16 November 2019 +### Fixed +- Update rebuild command to work with new docs locations (https://github.com/mpociot/laravel-apidoc-generator/pull/646) + +## [4.0.0] - Thursday, 7 November 2019 +### Added +- Added `headers` stage (https://github.com/mpociot/laravel-apidoc-generator/pull/624) +- Support for non-static docs, changed source files locations (https://github.com/mpociot/laravel-apidoc-generator/pull/608) +- Support for Eloquent API resources (https://github.com/mpociot/laravel-apidoc-generator/pull/601) +- `bindings` replaced by `@urlParam` annotation (https://github.com/mpociot/laravel-apidoc-generator/pull/599) +- Better support for arrays and objects in bodyParams (https://github.com/mpociot/laravel-apidoc-generator/pull/597) + +### Modified +- Postman collection now have the body as `raw` instead of `formdata`. (https://github.com/mpociot/laravel-apidoc-generator/pull/627) +- Nonexistent `@responseFile` annotations now show a warning and skip the route (https://github.com/mpociot/laravel-apidoc-generator/pull/620) +- Use symfony/var-exporter to export PHP arrays, ensuring short array syntax (https://github.com/mpociot/laravel-apidoc-generator/pull/615) +- Use single quotes in PHP example template (https://github.com/mpociot/laravel-apidoc-generator/pull/612) +- Transformer annotations are now given priority over all other response strategies (https://github.com/mpociot/laravel-apidoc-generator/pull/620) +- Made ResponseCalls strategy only execute if no successful responses exist. (https://github.com/mpociot/laravel-apidoc-generator/pull/605) +- Hide null responses in examples. (https://github.com/mpociot/laravel-apidoc-generator/pull/605) +- Made `responses` stage additive (https://github.com/mpociot/laravel-apidoc-generator/pull/605) +- Renamed `query` and `body` in `response_calls` config to `queryParams` and `bodyParams` (https://github.com/mpociot/laravel-apidoc-generator/pull/603) + +### Removed +- Removed `apply.response_calls.headers` in favour of `apply.headers` (https://github.com/mpociot/laravel-apidoc-generator/pull/603) +- Removed bindings in response_calls (https://github.com/mpociot/laravel-apidoc-generator/pull/599) + +## [3.17.1] - Thursday, 12 September 2019 +### Fixed +- ResponseCalls: Call Lumen application correctly since it does not use HttpKernel (https://github.com/mpociot/laravel-apidoc-generator/pull/585) +- Update usage of `clean*Parameters` in python template (https://github.com/mpociot/laravel-apidoc-generator/commit/02fb719d0d6c25e6ce72f30dc8b9604449061156) +- Bugfix: *really* exclude parameters from examples, not just send empty strings (https://github.com/mpociot/laravel-apidoc-generator/commit/762e2e1003d389d6e785d31144eca89c40515926, https://github.com/mpociot/laravel-apidoc-generator/commit/e54b474578b53f97f4737664a63131b315aaf82d) + +## [3.17.0] - Saturday, 7 September 2019 +### Added +- Switched to a plugin architecture that allows support for external strategies (https://github.com/mpociot/laravel-apidoc-generator/pull/570) + +### Changed +- Exclude Laravel Telescope routes when present (https://github.com/mpociot/laravel-apidoc-generator/pull/579) +- Set status code for transformer response from tag if present (https://github.com/mpociot/laravel-apidoc-generator/pull/581) +- Set status code for response call from actual response (https://github.com/mpociot/laravel-apidoc-generator/pull/581) + +## [3.16.3] - Thursday, 5 September 2019 +### Fixed +- Removed references to removed helper functions in 6.0 (https://github.com/mpociot/laravel-apidoc-generator/pull/576) + +## [3.16.2] - Wednesday, 4 September 2019 +### Fixed +- Support for Laravel 6 (https://github.com/mpociot/laravel-apidoc-generator/commit/f7dd8d19b75755763e8e20ab4025075eba5cd51a) + +## [3.16.1] - Wednesday, 4 September 2019 +### Added +- Use HTTPS in Postman collection if base_url is HTTPS (https://github.com/mpociot/laravel-apidoc-generator/pull/575) + +## [3.16.0] - Wednesday, 4 September 2019 +### Added +- Support for Laravel 6 (https://github.com/mpociot/laravel-apidoc-generator/pull/572) + +## [3.15.0] - Saturday, 31 August 2019 +### Added +- Ability to exclude a query or body parameter from being included in the example requests (https://github.com/mpociot/laravel-apidoc-generator/pull/552) + +## [3.14.0] - Saturday, 31 August 2019 +### Fixed +- Backwards compatibility for the changes to `@group` introduced in 3.12.0 (https://github.com/mpociot/laravel-apidoc-generator/commit/5647eda35ebb7f8aed35b31790c5f220b736e985) + +## [3.13.0] (deleted) + +## [3.12.0] - Sunday, 25 August 2019 +### Fixed +- Specifying an `@group` for a method no longer requires you to add the description. (https://github.com/mpociot/laravel-apidoc-generator/pull/556) +- Pass the verbosity level down to the Collision library. (https://github.com/mpociot/laravel-apidoc-generator/pull/556) + +## [3.11.0] - Friday, 9 August 2019 +### Added +- Support for query parameters in the bash template (https://github.com/mpociot/laravel-apidoc-generator/pull/545) +- Include query parameters and headers in the generated Postman collection (https://github.com/mpociot/laravel-apidoc-generator/pull/537) +- Include Python out of the box as example language (https://github.com/mpociot/laravel-apidoc-generator/pull/524) + +### Changed +- Moved nunomaduro/collision to "suggested" so it doesn't break PHP 7.0 (https://github.com/mpociot/laravel-apidoc-generator/commit/2f3a2144e1a4f1eb0229aea8b4d11707cb4aabbf) + +### Fixed +- Stopped using config helper inside config file (https://github.com/mpociot/laravel-apidoc-generator/pull/548) + +## [3.10.0] - Sunday, 23 June 2019 +### Added +- `--verbose` flag to show exception encountered when making response calls (https://github.com/mpociot/laravel-apidoc-generator/commit/dc987f296e5a3d073f56c67911b2cb61ae47e9dc) + ## [3.9.0] - Saturday, 8 June 2019 ### Modified - Postman collections and URLs in example requests now use the `apidoc.base_url` config variable (https://github.com/mpociot/laravel-apidoc-generator/pull/523) diff --git a/README.md b/README.md index dd7b865e..f5c6ed8f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Laravel API Documentation Generator -Automatically generate your API documentation from your existing Laravel/Lumen/[Dingo](https://github.com/dingo/api) routes. [Here's what the output looks like](http://marcelpociot.de/whiteboard/). +Automatically generate your API documentation from your existing Laravel/Lumen/[Dingo](https://github.com/dingo/api) routes. `php artisan apidoc:generate` @@ -12,10 +12,12 @@ Automatically generate your API documentation from your existing Laravel/Lumen/[ [![StyleCI](https://styleci.io/repos/57999295/shield?style=flat)](https://styleci.io/repos/57999295) ## Installation -PHP 7 and Laravel 5.5 or higher are required. +PHP 7.2 and Laravel/Lumen 5.7 or higher are required. + +> If your application does not meet these requirements, you can check out the 3.x branch for older releases. ```sh -composer require mpociot/laravel-apidoc-generator +composer require --dev mpociot/laravel-apidoc-generator ``` ### Laravel @@ -28,6 +30,7 @@ php artisan vendor:publish --provider="Mpociot\ApiDoc\ApiDocGeneratorServiceProv This will create an `apidoc.php` file in your `config` folder. ### Lumen +- When using Lumen, you will need to run `composer require mpociot/laravel-apidoc-generator` instead. - Register the service provider in your `bootstrap/app.php`: ```php @@ -41,7 +44,7 @@ $app->configure('apidoc'); ``` ## Documentation -Check out the documentation at [ReadTheDocs](http://laravel-apidoc-generator.readthedocs.io). +Check out the documentation at the [Beyond Code homepage](https://beyondco.de/docs/laravel-apidoc-generator/). ### License diff --git a/TODO.md b/TODO.md deleted file mode 100644 index aa508229..00000000 --- a/TODO.md +++ /dev/null @@ -1,6 +0,0 @@ -- Add tests for bindings and bindings prefixes -- Add tests for config overrides -- Add tests on output (deterministic) -- Bring `bindings` outside of `response_calls` -- Should `routes.*.apply.response_calls.headers` be replaced by `routes.*.apply.headers`? -- Implement debug flag diff --git a/body_params.png b/body_params.png deleted file mode 100644 index daa7afd9..00000000 Binary files a/body_params.png and /dev/null differ diff --git a/body_params_1.png b/body_params_1.png new file mode 100644 index 00000000..c1cecfe9 Binary files /dev/null and b/body_params_1.png differ diff --git a/body_params_2.png b/body_params_2.png new file mode 100644 index 00000000..eaaa79e9 Binary files /dev/null and b/body_params_2.png differ diff --git a/composer.json b/composer.json index cbc31be8..9249c973 100644 --- a/composer.json +++ b/composer.json @@ -15,21 +15,26 @@ } ], "require": { - "php": ">=7.0.0", - "fzaninotto/faker": "~1.8", - "illuminate/routing": "5.5.* || 5.6.* || 5.7.* || 5.8.*", - "illuminate/support": "5.5.* || 5.6.* || 5.7.* || 5.8.*", - "illuminate/console": "5.5.* || 5.6.* || 5.7.* || 5.8.*", - "mpociot/documentarian": "^0.2.0", + "php": ">=7.2.0", + "ext-json": "*", + "fzaninotto/faker": "^1.8", + "illuminate/console": "^5.7|^6.0|^7.0|^8.0", + "illuminate/routing": "^5.7|^6.0|^7.0|^8.0", + "illuminate/support": "^5.7|^6.0|^7.0|^8.0", + "league/flysystem": "^1.0", + "mpociot/documentarian": "^0.4.0", "mpociot/reflection-docblock": "^1.0.1", - "ramsey/uuid": "^3.8" + "nunomaduro/collision": "^3.0|^4.0|^5.0", + "ramsey/uuid": "^3.8|^4.0", + "symfony/var-exporter": "^4.0|^5.0" }, "require-dev": { - "orchestra/testbench": "3.5.* || 3.6.* || 3.7.*", - "phpunit/phpunit": "^6.0.0 || ^7.4.0", - "dingo/api": "2.0.0-alpha1", - "mockery/mockery": "^1.2.0", - "league/fractal": "^0.17.0" + "dms/phpunit-arraysubset-asserts": "^0.1.0", + "laravel/lumen-framework": "^5.7|^6.0|^7.0|^8.0", + "league/fractal": "^0.19.0", + "orchestra/testbench": "^3.7|^4.0|^5.0", + "phpstan/phpstan": "^0.11.15", + "phpunit/phpunit": "^8.0" }, "suggest": { "league/fractal": "Required for transformers support" @@ -45,13 +50,22 @@ } }, "scripts": { - "test-ci": "phpunit --coverage-clover=coverage.xml" + "lint": "phpstan analyse -c ./phpstan.neon src", + "test": "phpunit --stop-on-failure --exclude-group dingo", + "test-ci": "phpunit --exclude-group dingo" }, "extra": { "laravel": { "providers": [ "Mpociot\\ApiDoc\\ApiDocGeneratorServiceProvider" ] - } + }, + "branch-alias": { + "dev-v4": "4.x-dev" + } + }, + "config": { + "preferred-install": "dist", + "sort-packages": true } } diff --git a/config/apidoc.php b/config/apidoc.php index df21372e..8ae737be 100644 --- a/config/apidoc.php +++ b/config/apidoc.php @@ -1,26 +1,65 @@ 'static', /* - * The output path for the generated documentation. - * This path should be relative to the root of your application. + * Static output folder: HTML documentation and assets will be generated in this folder. */ - 'output' => 'public/docs', + 'output_folder' => 'public/docs', + + /* + * Settings for `laravel` type output. + */ + 'laravel' => [ + /* + * Whether to automatically create a docs endpoint for you to view your generated docs. + * If this is false, you can still set up routing manually. + */ + 'autoload' => false, + + /* + * URL path to use for the docs endpoint (if `autoload` is true). + * + * By default, `/doc` opens the HTML page, and `/doc.json` downloads the Postman collection. + */ + 'docs_url' => '/doc', + + /* + * Middleware to attach to the docs endpoint (if `autoload` is true). + */ + 'middleware' => [], + ], /* * The router to be used (Laravel or Dingo). */ 'router' => 'laravel', + /* + * The storage to be used when generating assets. + * By default, uses 'local'. If you are using Laravel Vapor, please use S3 and make sure + * the correct bucket is correctly configured in the .env file + */ + 'storage' => 'local', + /* * The base URL to be used in examples and the Postman collection. * By default, this will be the value of config('app.url'). */ - 'base_url' => config('app.url'), + 'base_url' => null, /* * Generate a Postman collection in addition to HTML docs. + * For 'static' docs, the collection will be generated to public/docs/collection.json. + * For 'laravel' docs, it will be generated to storage/app/apidoc/collection.json. + * The `ApiDoc::routes()` helper will add routes for both the HTML and the Postman collection. */ 'postman' => [ /* @@ -37,6 +76,12 @@ * The description for the exported Postman collection. */ 'description' => null, + + /* + * The "Auth" section that should appear in the postman collection. See the schema docs for more information: + * https://schema.getpostman.com/json/collection/v2.0.0/docs/index.html + */ + 'auth' => null, ], /* @@ -103,6 +148,8 @@ * Specify headers to be added to the example requests */ 'headers' => [ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', // 'Authorization' => 'Bearer {token}', // 'Api-Version' => 'v2', ], @@ -119,20 +166,6 @@ */ 'methods' => ['GET'], - /* - * For URLs which have parameters (/users/{user}, /orders/{id?}), - * specify what values the parameters should be replaced with. - * Note that you must specify the full parameter, - * including curly brackets and question marks if any. - * - * You may also specify the preceding path, to allow for variations, - * for instance 'users/{id}' => 1 and 'apps/{id}' => 'htTviP'. - * However, there must only be one parameter per path. - */ - 'bindings' => [ - // '{user}' => 1, - ], - /* * Laravel config variables which should be set for the API call. * This is a good place to ensure that notifications, emails @@ -145,15 +178,6 @@ // 'service.key' => 'value', ], - /* - * Headers which should be sent with the API call. - */ - 'headers' => [ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - // 'key' => 'value', - ], - /* * Cookies which should be sent with the API call. */ @@ -164,14 +188,14 @@ /* * Query parameters which should be sent with the API call. */ - 'query' => [ + 'queryParams' => [ // 'key' => 'value', ], /* * Body parameters which should be sent with the API call. */ - 'body' => [ + 'bodyParams' => [ // 'key' => 'value', ], ], @@ -179,6 +203,31 @@ ], ], + 'strategies' => [ + 'metadata' => [ + \Mpociot\ApiDoc\Extracting\Strategies\Metadata\GetFromDocBlocks::class, + ], + 'urlParameters' => [ + \Mpociot\ApiDoc\Extracting\Strategies\UrlParameters\GetFromUrlParamTag::class, + ], + 'queryParameters' => [ + \Mpociot\ApiDoc\Extracting\Strategies\QueryParameters\GetFromQueryParamTag::class, + ], + 'headers' => [ + \Mpociot\ApiDoc\Extracting\Strategies\RequestHeaders\GetFromRouteRules::class, + ], + 'bodyParameters' => [ + \Mpociot\ApiDoc\Extracting\Strategies\BodyParameters\GetFromBodyParamTag::class, + ], + 'responses' => [ + \Mpociot\ApiDoc\Extracting\Strategies\Responses\UseTransformerTags::class, + \Mpociot\ApiDoc\Extracting\Strategies\Responses\UseResponseTag::class, + \Mpociot\ApiDoc\Extracting\Strategies\Responses\UseResponseFileTag::class, + \Mpociot\ApiDoc\Extracting\Strategies\Responses\UseApiResourceTags::class, + \Mpociot\ApiDoc\Extracting\Strategies\Responses\ResponseCalls::class, + ], + ], + /* * Custom logo path. The logo will be copied from this location * during the generate process. Set this to false to use the default logo. @@ -198,7 +247,7 @@ /* * Example requests for each endpoint will be shown in each of these languages. - * Supported options are: bash, javascript, php + * Supported options are: bash, javascript, php, python * You can add a language of your own, but you must publish the package's views * and define a corresponding view for it in the partials/example-requests directory. * See https://laravel-apidoc-generator.readthedocs.io/en/latest/generating-documentation.html @@ -234,4 +283,11 @@ * */ 'faker_seed' => null, + + /* + * If you would like to customize how routes are matched beyond the route configuration you may + * declare your own implementation of RouteMatcherInterface + * + */ + 'routeMatcher' => \Mpociot\ApiDoc\Matching\RouteMatcher::class, ]; diff --git a/dingo.composer.json b/dingo.composer.json new file mode 100644 index 00000000..83004b63 --- /dev/null +++ b/dingo.composer.json @@ -0,0 +1,71 @@ +{ + "name": "mpociot/laravel-apidoc-generator", + "license": "MIT", + "description": "Generate beautiful API documentation from your Laravel application", + "keywords": [ + "API", + "Documentation", + "Laravel" + ], + "homepage": "http://github.com/mpociot/laravel-apidoc-generator", + "authors": [ + { + "name": "Marcel Pociot", + "email": "m.pociot@gmail.com" + } + ], + "require": { + "php": ">=7.2.0", + "ext-json": "*", + "fzaninotto/faker": "^1.8", + "illuminate/console": "^5.7|^6.0|^7.0|^8.0", + "illuminate/routing": "^5.7|^6.0|^7.0|^8.0", + "illuminate/support": "^5.7|^6.0|^7.0|^8.0", + "league/flysystem": "^1.0", + "mpociot/documentarian": "^0.4.0", + "mpociot/reflection-docblock": "^1.0.1", + "nunomaduro/collision": "^3.0|^4.0|^5.0", + "ramsey/uuid": "^3.8|^4.0", + "symfony/var-exporter": "^4.0|^5.0" + }, + "require-dev": { + "dingo/api": "^2.3", + "dms/phpunit-arraysubset-asserts": "^0.1.0", + "league/fractal": "^0.17.0", + "orchestra/testbench": "^3.7|^4.0|^5.0", + "phpstan/phpstan": "^0.11.15", + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "league/fractal": "Required for transformers support" + }, + "autoload": { + "psr-4": { + "Mpociot\\ApiDoc\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Mpociot\\ApiDoc\\Tests\\": "tests/" + } + }, + "scripts": { + "lint": "phpstan analyse -c ./phpstan.neon src", + "test": "phpunit --stop-on-failure --group dingo", + "test-ci": "phpunit --group dingo" + }, + "extra": { + "laravel": { + "providers": [ + "Mpociot\\ApiDoc\\ApiDocGeneratorServiceProvider" + ] + }, + "branch-alias": { + "dev-v4": "4.x-dev" + } + }, + "config": { + "preferred-install": "dist", + "sort-packages": true + } +} diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 298ea9e2..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/_index.md b/docs/_index.md new file mode 100644 index 00000000..8dcb342b --- /dev/null +++ b/docs/_index.md @@ -0,0 +1,4 @@ +--- +packageName: Laravel API Documentation Generator +githubUrl: https://github.com/mpociot/laravel-apidoc-generator +--- \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 58bf09e5..00000000 --- a/docs/conf.py +++ /dev/null @@ -1,61 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) -import recommonmark -from recommonmark.transform import AutoStructify - -# -- Project information ----------------------------------------------------- - -project = 'laravel-apidoc-generator' -copyright = '2019, Marcel Pociot' -author = 'Marcel Pociot' - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = ['recommonmark'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -master_doc = 'index' - -# app setup hook -def setup(app): - app.add_config_value('recommonmark_config', { - 'auto_toc_tree_section': 'Contents', - }, True) - app.add_transform(AutoStructify) diff --git a/docs/extending/_index.md b/docs/extending/_index.md new file mode 100644 index 00000000..cf3041a7 --- /dev/null +++ b/docs/extending/_index.md @@ -0,0 +1,4 @@ +--- +title: Extending +order: 2 +--- diff --git a/docs/extending/plugins.md b/docs/extending/plugins.md new file mode 100644 index 00000000..ac3b343a --- /dev/null +++ b/docs/extending/plugins.md @@ -0,0 +1,178 @@ +--- +title: Plugins +order: 1 +--- +# Extending functionality with plugins +You can use plugins to alter how the Generator fetches data about your routes. For instance, suppose all your routes have a body parameter `organizationId`, and you don't want to annotate this with `@queryParam` on each method. You can create a plugin that adds this to all your body parameters. Let's see how to do this. + +## The stages of route processing +Route processing is performed in six stages: +- metadata (this covers route `title`, route `description`, route `groupName`, route `groupDescription`, and authentication status (`authenticated`)) +- urlParameters +- queryParameters +- headers (headers to be added to example request and response calls) +- bodyParameters +- responses + +For each stage, the Generator attempts the specified strategies to fetch data. The Generator will call of the strategies configured, progressively combining their results together before to produce the final output of that stage. + +There are a number of strategies included with the package, so you don't have to set up anything to get it working. + +> Note: The included ResponseCalls strategy is designed to stop if a response with a 2xx status code has already been gotten via any other strategy. + +## Strategies +To create a strategy, create a class that extends `\Mpociot\ApiDoc\Extracting\Strategies\Strategy`. + +The `__invoke` method of the strategy is where you perform your actions and return data. It receives the following arguments: +- the route (instance of `\Illuminate\Routing\Route`) +- the controller class handling the route (`\ReflectionClass`) +- the controller method (`\ReflectionMethod $method`) + - the rules specified in the apidoc.php config file for the group this route belongs to, under the `apply` section (array) + - the context. This contains all data for the route that has been parsed thus far in the previous stages. This means, by the `responses` stage, the context will contain the following keys: `metadata`, `bodyParameters` and `queryParameters`. + + Here's what your strategy in our example would look like: + + ```php +use Illuminate\Routing\Route; +use Mpociot\ApiDoc\Extracting\Strategies\Strategy; + +class AddOrganizationIdBodyParameter extends Strategy +{ + public function __invoke(Route $route, \ReflectionClass $controller, \ReflectionMethod $method, array $routeRules, array $context = []) + { + return [ + 'organizationId' => [ + 'type' => 'integer', + 'description' => 'The ID of the organization', + 'required' => true, + 'value' => 2, + ] + ]; + } +} +``` + +The last thing to do is to register the strategy. Strategies are registered in a `strategies` key in the `apidoc.php` file. Here's what the file looks like by default: + +```php +... + 'strategies' => [ + 'metadata' => [ + \Mpociot\ApiDoc\Extracting\Strategies\Metadata\GetFromDocBlocks::class, + ], + 'urlParameters' => [ + \Mpociot\ApiDoc\Extracting\Strategies\UrlParameters\GetFromUrlParamTag::class, + ], + 'queryParameters' => [ + \Mpociot\ApiDoc\Extracting\Strategies\QueryParameters\GetFromQueryParamTag::class, + ], + 'headers' => [ + \Mpociot\ApiDoc\Extracting\Strategies\RequestHeaders\GetFromRouteRules::class, + ], + 'bodyParameters' => [ + \Mpociot\ApiDoc\Extracting\Strategies\BodyParameters\GetFromBodyParamTag::class, + ], + 'responses' => [ + \Mpociot\ApiDoc\Extracting\Strategies\Responses\UseTransformerTags::class, + \Mpociot\ApiDoc\Extracting\Strategies\Responses\UseResponseTag::class, + \Mpociot\ApiDoc\Extracting\Strategies\Responses\UseResponseFileTag::class, + \Mpociot\ApiDoc\Extracting\Strategies\Responses\UseApiResourceTags::class, + \Mpociot\ApiDoc\Extracting\Strategies\Responses\ResponseCalls::class, + ], + ], +... +``` + +You can add, replace or remove strategies from here. In our case, we're adding our bodyParameter strategy: + +```php + + 'bodyParameters' => [ + \Mpociot\ApiDoc\Extracting\Strategies\BodyParameters\GetFromBodyParamTag::class, + AddOrganizationIdBodyParameter::class, + ], +``` + +And we're done. Now, when we run `php artisan docs:generate`, all our routes will have this bodyParameter added. + + +We could go further and modify our strategy so it doesn't add this parameter if the route is a GET route or is authenticated: + +```php +public function __invoke(Route $route, \ReflectionClass $controller, \ReflectionMethod $method, array $routeRules, array $context = []) +{ + if (in_array('GET', $route->methods()) { + return null; + } + + if ($context['metadata']['authenticated']) { + return null; + } + + return [ + 'organizationId' => [ + 'type' => 'integer', + 'description' => 'The ID of the organization', + 'required' => true, + 'value' => 2, + ] + ]; +} +``` + +> Note: If you would like a parameter (body or query) to be included in the documentation but excluded from examples, set its `value` property to `null`. + +The strategy class also has access to the current apidoc configuration via its `config` property. For instance, you can retrieve the default group with `$this->config->get('default_group')`. + +You are also provided with the instance pproperty `stage`, which is set to the name of the currently executing stage. + + +## Utilities +You have access to a number of tools when developing strategies. They include: + +- The `RouteDocBlocker` class (in the `\Mpociot\ApiDoc\Extracting` namespace) has a single public static method, `getDocBlocksFromRoute(Route $route)`. It allows you to retrieve the docblocks for a given route. It returns an array of with two keys: `method` and `class` containing the docblocks for the method and controller handling the route respectively. Both are instances of `\Mpociot\Reflection\DocBlock`. + +- The `ParamsHelper` trait (in the `\Mpociot\ApiDoc\Extracting` namespace) can be included in your strategies. It contains a number of useful methods for working with parameters, including type casting and generating dummy values. + +## API +Each strategy class must implement the __invoke method with the parameters as described above. This method must return the needed data for the intended stage, or `null` to indicate failure. +- In the `metadata` stage, strategies should return an array. These are the expected keys (you may omit some, or all): + +``` +'groupName' +'groupDescription' +'title' +'description' +'authenticated' // boolean +``` + +- In the `bodyParameters` and `queryParameters` stages, you can return an array with arbitrary keys. These keys will serve as the names of your parameters. Array keys can be indicated with Laravel's dot notation. The value of each key should be an array with the following keys: + +``` +'type', // Only valid in bodyParameters +'description', +'required', // boolean +'value', // An example value for the parameter +``` +- In the `responses` stage, your strategy should return an array containing the responses for different status codes. Each item in the array should be an array representing the response with a `status` key containing the HTTP status code, and a `content` key a string containing the response. For example: + +```php + + public function __invoke(Route $route, \ReflectionClass $controller, \ReflectionMethod $method, array $routeRules, array $context = []) + { + return [ + [ + 'content' => "Haha", + 'status' => 201 + ], + [ + 'content' => "Nope", + 'status' => 404 + ], + ] + } +``` + +Responses are _additive_. This means all the responses returned from each stage are added to the `responses` array. But note that the `ResponseCalls` strategy will only attempt to fetch a response if there are no responses with a status code of 2xx already. + +- In the `headers` stage, you can return an array of headers. You may also negate existing headers by providing `false` as the header value. diff --git a/docs/getting-started/_index.md b/docs/getting-started/_index.md new file mode 100644 index 00000000..7f67f577 --- /dev/null +++ b/docs/getting-started/_index.md @@ -0,0 +1,4 @@ +--- +title: Getting Started +order: 1 +--- diff --git a/docs/config.md b/docs/getting-started/configuration.md similarity index 64% rename from docs/config.md rename to docs/getting-started/configuration.md index bb25f846..b2d4c640 100644 --- a/docs/config.md +++ b/docs/getting-started/configuration.md @@ -1,29 +1,61 @@ +--- +title: Configuration +order: 2 +--- # Configuration -Before you can generate your documentation, you'll need to configure a few things in your `config/apidoc.php`. If you aren't sure what an option does, it's best to leave it set to the default. If you don't have this config file, see the [installation instructions](index.html#installation). +Before you can generate your documentation, you'll need to configure a few things in your `config/apidoc.php` file. -## `output` -This is the file path where the generated documentation will be written to. Note that the documentation is generated as static HTML and CSS assets, so the route is accessed directly, and not via the Laravel routing mechanism. This path should be relative to the root of your application. Default: **public/docs** +If you aren't sure what an option does, it's best to leave it set to the default. -## `router` +## type +This is the type of documentation output to generate. + +`static` will generate a static HTML page in the `public/docs` folder, so anyone can visit your documentation page by going to {yourapp.domain}/docs. + +`laravel` will generate the documentation as a Blade view within the `resources/views/apidoc` folder, so you can add routing and authentication to your liking. + +> In both instances, the source markdown file will be generated in `resources/docs/source`. + +### laravel +If you're using `laravel` type output, this package can automatically set up an endpoint for you to view your generated docs. You can configure this here. + +### autoload +Set this to `true` if you want the documentation endpoint to be automatically set up for you. Default: `false` + +You may, of course, use your own routing instead of using `autoload`. + +### docs_url +The path for the documentation endpoint (if `autoload` is true). Your Postman collection (if you have that enabled) will be at this path + '.json' (eg `/doc.json`). Default: `/doc` + +> Note: There is currently a known issue with using `/docs` as the path for `laravel` docs. You should not use it, as it conflicts with the folder structure in the `public` folder and may confuse the webserver. + +## middleware +Here, you can specify middleware to be attached to the documentation endpoint (if `autoload` is true). + +## router The router to use when processing your routes (can be Laravel or Dingo. Defaults to **Laravel**) -## `base_url` +## base_url The base URL to be used in examples and the Postman collection. By default, this will be the value of config('app.url'). -## `postman` +## postman This package can automatically generate a Postman collection for your routes, along with the documentation. This section is where you can configure (or disable) that. -### `enabled` +For `static` docs (see [type](#type)), the collection will be created in `public/docs/collection.json`, so it can be accessed by visiting {yourapp.domain}/docs/colllection.json. + +For `laravel` docs, the collection will be generated to `storage/app/apidoc/collection.json`. The `ApiDoc::routes()` helper will add a `/docs.json` endpoint to fetch it.. + +### enabled Whether or not to generate a Postman API collection. Default: **true** -### `name` +### name The name for the exported Postman collection. If you leave this as null, this package will default to `config('app.name')." API"`. -### `description` +### description The description for the generated Postman collection. -## `logo` +## logo You can specify a custom logo to be used on the generated documentation. Set the `logo` option to an absolute path pointing to your logo file. For example: ``` 'logo' => resource_path('views') . '/api/logo.png' @@ -31,20 +63,24 @@ You can specify a custom logo to be used on the generated documentation. Set the If you want to use this, please note that the image size must be 230 x 52. -## `default_group` -When [documenting your api](documenting.md), you use `@group` annotations to group API endpoints. Endpoints which do not have a ggroup annotation will be grouped under the `default_group`. Defaults to **"general"**. +## default_group +When [documenting your api](/docs/laravel-apidoc-generator/getting-started/documenting-your-api), you use `@group` annotations to group API endpoints. Endpoints which do not have a group annotation will be grouped under the `default_group`. Defaults to **"general"**. -## `example_languages` -For each endpoint, an example request is shown in each of the languages specified in this array. Currently only `bash`, `javascript` and `php` are supported. You can add your own language, but you must also define the corresponding view (see [Specifying languages for examples](generating-documentation.html#specifying-language-for-examples)). Default: `["bash", "javascript"]` +## example_languages +For each endpoint, an example request is shown in each of the languages specified in this array. Currently only `bash`, `javascript`, `php` and `python` are supported. You can add your own language, but you must also define the corresponding view (see [Specifying languages for examples](/docs/laravel-apidoc-generator/getting-started/generating-documentation)). Default: `["bash", "javascript"]` -## `faker_seed` -When generating example requests, this package uses fzanninoto/faker to generate random values. If you would like the package to generate the same example values for parameters on each run, set this to any number (eg. 1234). (Note: alternatively, you can set example values for parameters when [documenting them.](documenting.html#specifying-request-parameters)) +## faker_seed +When generating example requests, this package uses fzanninoto/faker to generate random values. If you would like the package to generate the same example values for parameters on each run, set this to any number (eg. 1234). (Note: alternatively, you can set example values for parameters when [documenting them.](/docs/laravel-apidoc-generator/getting-started/documenting-your-api)) + +## routeMatcher +The route matcher class provides the algorithm that determines what routes should be documented. The default matcher used is the included `\Mpociot\ApiDoc\Matching\RouteMatcher::class`, and you can provide your own custom implementation if you wish to programmatically change the algorithm. The provided matcher must be an instance of the `RouteMatcherInterface`. -## `fractal` -This section only applies if you're using [Transformers]() for your API, and documenting responses with `@transformer` and `@transformerCollection`. Here, you configure how responses are transformed. +## fractal +This section only applies if you're using Transformers for your API, and documenting responses with `@transformer` and `@transformerCollection`. Here, you configure how responses are transformed. > Note: using transformers requires league/fractal package. Run `composer require league/fractal to install + ### serializer If you are using a custom serializer with league/fractal, you can specify it here. league/fractal comes with the following serializers: - \League\Fractal\Serializer\ArraySerializer::class @@ -53,16 +89,14 @@ If you are using a custom serializer with league/fractal, you can specify it he Leave this as null to use no serializer or return a simple JSON. -## `routes` +## routes The `routes` section is an array of items, describing what routes in your application that should have documentation generated for them. Each item in the array contains rules about what routes belong in that group, and what rules to apply to them. This allows you to apply different settings to different routes. > Note: This package does not work with Closure-based routes. If you want your route to be captured by this package, you need a controller. -Each item in the `routes` array (a route group) has keys which are explained below. We'll use this sample route definition for a Laravel app to demonstarte them: +Each item in the `routes` array (a route group) has keys which are explained below. We'll use this sample route definition for a Laravel app to demonstrate them: ```php - 'api.acme.co'], function () { Route::get('/apps', 'AppController@listApps') ->name('apps.list'); @@ -87,14 +121,13 @@ Route::group(['domain' => 'status.acme.co'], function () { }); ``` -### `match` +### match In this section, you define the rules that will be used to determine what routes in your application fall into this group. There are three kinds of rules defined here (keys in the `match` array): -#### `domains` -This key takes an array of domain names as its value. Only routes which are defined on the domains specified here will be matched as part of this group. For instance, in our sample routes above, we may wish to apply different settings to documentation based on the domains. For instance, the routes on the `api.acme.co` domain need authentication, while those on the other domains do not. We can searate them into two groups like this: +### domains +This key takes an array of domain names as its value. Only routes which are defined on the domains specified here will be matched as part of this group. For instance, in our sample routes above, we may wish to apply different settings to documentation based on the domains. For instance, the routes on the `api.acme.co` domain need authentication, while those on the other domains do not. We can separate them into two groups like this: ```php - The `domains` and `prefixes` keys are both required for all route groups. -#### `versions` +### versions > This section only applies if you're using Dingo Router When using Dingo's Router, all routes must be specified inside versions. This means that you must specify the versions to be matched along with the domains and prefixes when describing a route group. Note that wildcards in `versions` are not supported; you must list out all your versions explicitly. Example: ```php - ['users/*']` to exclude all routes with URLs matching the pattern. -### `apply` +### apply After defining the routes in `match` (and `include` or `exclude`), `apply` is where you specify the settings to be applied to those routes when generating documentation. There are a bunch of settings you can tweak here: -#### `headers` -Like we've demonstrated above, any headers you specify here will be added to the headers shown in the example requests in your documenation. Headers are specified as key => value strings. +### headers +Like we've demonstrated above, any headers you specify here will be added to the headers shown in the example requests in your documentation. They will also be included in ["response calls"](/docs/laravel-apidoc-generator/getting-started/documenting-your-api). Headers are specified as key => value strings. -#### `response_calls` -These are the settings that will be applied when making ["response calls"](documenting.html#generating-responses-automatically). See the linked section for details. +### response_calls +These are the settings that will be applied when making ["response calls"](/docs/laravel-apidoc-generator/getting-started/documenting-your-api). See the linked section for details. diff --git a/docs/documenting.md b/docs/getting-started/documenting-your-api.md similarity index 62% rename from docs/documenting.md rename to docs/getting-started/documenting-your-api.md index e85897ca..c11a4600 100644 --- a/docs/documenting.md +++ b/docs/getting-started/documenting-your-api.md @@ -1,8 +1,12 @@ +--- +title: Documenting Your API +order: 4 +--- # Documenting Your API This package generates documentation from your code using mainly annotations (in doc block comments). ## Grouping endpoints -All endpoints are grouped for easy organization. Using `@group` in a controller doc block creates a Group within the API documentation. All routes handled by that controller will be grouped under this group in the table of conetns shown in the sidebar. +All endpoints are grouped for easy organization. Using `@group` in a controller doc block creates a Group within the API documentation. All routes handled by that controller will be grouped under this group in the table of contents shown in the sidebar. The short description after the `@group` should be unique to allow anchor tags to navigate to this section. A longer description can be included below. Custom formatting and `