Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [3.0] - unreleased
### Added
- Official Lumen support (https://github.com/mpociot/laravel-apidoc-generator/pull/382)
- `@queryParam` annotation (https://github.com/mpociot/laravel-apidoc-generator/pull/383)
- `@bodyParam` annotation (https://github.com/mpociot/laravel-apidoc-generator/pull/362, https://github.com/mpociot/laravel-apidoc-generator/pull/366)
- `@authenticated` annotation (https://github.com/mpociot/laravel-apidoc-generator/pull/369)
- Ability to override the controller `@group` from the method. (https://github.com/mpociot/laravel-apidoc-generator/pull/372)
Expand Down
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,31 @@ Automatically generate your API documentation from your existing Laravel/Lumen/[
> Note: this is the documentation for version 3, which changes significantly from version 2. if you're on v2, you can check out its documentation [here](https://github.com/mpociot/laravel-apidoc-generator/blob/2.x/README.md). We strongly recommend you upgrade, though, as v3 is more robust and fixes a lot of the problems with v2.

## Installation
> Note: version 3.x requires PHP 7 and Laravel 5.5 or higher.
> Note: PHP 7 and Laravel 5.5 or higher are required.

```sh
$ composer require mpociot/laravel-apidoc-generator:dev-master
```

Then publish the config file by running:
### Laravel
Publish the config file by running:

```bash
php artisan vendor:publish --provider=Mpociot\ApiDoc\ApiDocGeneratorServiceProvider --tag=config
```
This will create an `apidoc.php` file in your `config` folder.

### Lumen
- Register the service provider in your `bootstrap/app.php`:
```php
$app->register(\Mpociot\ApiDoc\ApiDocGeneratorServiceProvider::class);
```
- Copy the config file from `vendor/mpociot/laravel-apidoc-generator/config/apidoc.php` to your project as `config/apidoc.php`. Then add to your `bootstrap/app.php`:
```php
$app->configure('apidoc');
```


## Usage
Before you can generate your documentation, you'll need to configure a few things in your `config/apidoc.php`.
- `output`
Expand All @@ -38,6 +50,9 @@ Set this option to true if you want a Postman collection to be generated along w
- `router`
The router to use when processing the route (can be Laravel or Dingo. Defaults to **Laravel**)

- `logo`
You can specify your custom logo to be used on the generated documentation. Set the `logo` option to an absolute path pointing to your logo file.

- `routes`
This is where you specify what rules documentation should be generated for. You specify routes to be parsed by defining conditions that the routes should meet and rules that should be applied when generating documentation. These conditions and rules are specified in groups, allowing you to apply different rules to different routes.

Expand Down Expand Up @@ -174,7 +189,7 @@ class UserController extends Controller

To specify a list of valid parameters your API route accepts, use the `@bodyParam` and `@queryParam` annotations.
- The `@bodyParam` annotation takes the name of the parameter, its type, an optional "required" label, and then its description.
- The `@queryParam` annotation (coming soon!) takes the name of the parameter, an optional "required" label, and then its description
- The `@queryParam` annotation takes the name of the parameter, an optional "required" label, and then its description


```php
Expand All @@ -189,6 +204,16 @@ public function createPost()
{
// ...
}

/**
* @queryParam sort Field to sort by
* @queryParam page The page number to return
* @queryParam fields required The fields to include
*/
public function listPosts()
{
// ...
}
```

They will be included in the generated documentation text and example requests.
Expand Down Expand Up @@ -274,7 +299,7 @@ If you don't specify an example response using any of the above means, this pack
- By default, response calls are only made for GET routes, but you can configure this. Set the `methods` key to an array of methods or '*' to mean all methods. Leave it as an empty array to turn off response calls for that route group.
- Parameters in URLs (example: `/users/{user}`, `/orders/{id?}`) will be replaced with '1' by default. You can configure this, however.Put the parameter names (including curly braces and question marks) as the keys and their replacements as the values in the `bindings` key.
- You can configure environment variables (this is useful so you can prevent external services like notifications from being triggered). By default the APP_ENV is set to 'documentation'. You can add more variables in the `env` key.
- You can also configure what headers, query parameters and body parameters should be sent when making the request (the `headers`, `query`, and `body` keys respectively).
- By default, the package will generate dummy values for your documented body and query parameters and send in the request. You can configure what headers and additional query and parameters should be sent when making the request (the `headers`, `query`, and `body` keys respectively).


### Postman collections
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/GenerateDocumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
use Illuminate\Console\Command;
use Mpociot\Reflection\DocBlock;
use Illuminate\Support\Collection;
use Mpociot\ApiDoc\Tools\Generator;
use Mpociot\ApiDoc\Tools\RouteMatcher;
use Illuminate\Support\Facades\Storage;
use Mpociot\ApiDoc\Generators\Generator;
use Mpociot\Documentarian\Documentarian;
use Mpociot\ApiDoc\Postman\CollectionWriter;

Expand Down
23 changes: 17 additions & 6 deletions src/Generators/Generator.php → src/Tools/Generator.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<?php

namespace Mpociot\ApiDoc\Generators;
namespace Mpociot\ApiDoc\Tools;

use Faker\Factory;
use ReflectionClass;
use ReflectionMethod;
use Illuminate\Routing\Route;
use Mpociot\Reflection\DocBlock;
use Mpociot\Reflection\DocBlock\Tag;
use Mpociot\ApiDoc\Tools\ResponseResolver;

class Generator
{
Expand Down Expand Up @@ -47,7 +46,13 @@ public function processRoute(Route $route, array $rulesToApply = [])

$routeGroup = $this->getRouteGroup($controller, $method);
$docBlock = $this->parseDocBlock($method);
$content = ResponseResolver::getResponse($route, $docBlock['tags'], $rulesToApply);
$bodyParameters = $this->getBodyParametersFromDocBlock($docBlock['tags']);
$queryParameters = $this->getQueryParametersFromDocBlock($docBlock['tags']);
$content = ResponseResolver::getResponse($route, $docBlock['tags'], [
'rules' => $rulesToApply,
'body' => $bodyParameters,
'query' => $queryParameters,
]);

$parsedRoute = [
'id' => md5($this->getUri($route).':'.implode($this->getMethods($route))),
Expand All @@ -56,8 +61,8 @@ public function processRoute(Route $route, array $rulesToApply = [])
'description' => $docBlock['long'],
'methods' => $this->getMethods($route),
'uri' => $this->getUri($route),
'bodyParameters' => $this->getBodyParametersFromDocBlock($docBlock['tags']),
'queryParameters' => $this->getQueryParametersFromDocBlock($docBlock['tags']),
'bodyParameters' => $bodyParameters,
'queryParameters' => $queryParameters,
'authenticated' => $this->getAuthStatusFromDocBlock($docBlock['tags']),
'response' => $content,
'showresponse' => ! empty($content),
Expand Down Expand Up @@ -132,7 +137,13 @@ protected function getQueryParametersFromDocBlock(array $tags)
$required = trim($required) == 'required' ? true : false;
}

return [$name => compact('description', 'required')];
if (str_contains($description, ['number', 'count', 'page'])) {
$value = $this->generateDummyValue('integer');
} else {
$value = $this->generateDummyValue('string');
}

return [$name => compact('description', 'required', 'value')];
})->toArray();

return $parameters;
Expand Down
8 changes: 4 additions & 4 deletions src/Tools/ResponseResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,21 @@ public function __construct(Route $route)
$this->route = $route;
}

private function resolve(array $tags, array $rulesToApply)
private function resolve(array $tags, array $routeProps)
{
$response = null;
foreach (static::$strategies as $strategy) {
$strategy = new $strategy();
$response = $strategy($this->route, $tags, $rulesToApply);
$response = $strategy($this->route, $tags, $routeProps);
if (! is_null($response)) {
return $this->getResponseContent($response);
}
}
}

public static function getResponse($route, $tags, $rulesToApply)
public static function getResponse($route, $tags, $routeProps)
{
return (new static($route))->resolve($tags, $rulesToApply);
return (new static($route))->resolve($tags, $routeProps);
}

/**
Expand Down
19 changes: 12 additions & 7 deletions src/Tools/ResponseStrategies/ResponseCallStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
*/
class ResponseCallStrategy
{
public function __invoke(Route $route, array $tags, array $rulesToApply)
public function __invoke(Route $route, array $tags, array $routeProps)
{
$rulesToApply = $rulesToApply['response_calls'] ?? [];
$rulesToApply = $routeProps['rules']['response_calls'] ?? [];
if (! $this->shouldMakeApiCall($route, $rulesToApply)) {
return;
}

$this->configureEnvironment($rulesToApply);
$request = $this->prepareRequest($route, $rulesToApply);
$request = $this->prepareRequest($route, $rulesToApply, $routeProps['body'], $routeProps['query']);
try {
$response = $this->makeApiCall($request);
} catch (\Exception $e) {
Expand All @@ -38,15 +38,20 @@ private function configureEnvironment(array $rulesToApply)
$this->setEnvironmentVariables($rulesToApply['env'] ?? []);
}

private function prepareRequest(Route $route, array $rulesToApply)
private function prepareRequest(Route $route, array $rulesToApply, array $bodyParams, array $queryParams)
{
$uri = $this->replaceUrlParameterBindings($route, $rulesToApply['bindings'] ?? []);
$routeMethods = $this->getMethods($route);
$method = array_shift($routeMethods);
$request = Request::create($uri, $method, [], [], [], $this->transformHeadersToServerVars($rulesToApply['headers'] ?? []));
$request = $this->addHeaders($request, $route, $rulesToApply['headers'] ?? []);
$request = $this->addQueryParameters($request, $rulesToApply['query'] ?? []);
$request = $this->addBodyParameters($request, $rulesToApply['body'] ?? []);

// Mix in parsed parameters with manually specified parameters.
$queryParams = collect($queryParams)->map->value->merge($rulesToApply['query'] ?? [])->toArray();
$bodyParams = collect($bodyParams)->map->value->merge($rulesToApply['body'] ?? [])->toArray();

$request = $this->addQueryParameters($request, $queryParams);
$request = $this->addBodyParameters($request, $bodyParams);

return $request;
}
Expand Down Expand Up @@ -188,7 +193,7 @@ private function makeApiCall(Request $request)
*
* @return \Symfony\Component\HttpFoundation\Response
*/
private function callLaravelRoute($request): \Symfony\Component\HttpFoundation\Response
private function callLaravelRoute(Request $request): \Symfony\Component\HttpFoundation\Response
{
$kernel = app(\Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle($request);
Expand Down
2 changes: 1 addition & 1 deletion src/Tools/ResponseStrategies/ResponseTagStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/
class ResponseTagStrategy
{
public function __invoke(Route $route, array $tags, array $rulesToApply)
public function __invoke(Route $route, array $tags, array $routeProps)
{
return $this->getDocBlockResponse($tags);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Tools/ResponseStrategies/TransformerTagsStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
class TransformerTagsStrategy
{
public function __invoke(Route $route, array $tags, array $rulesToApply)
public function __invoke(Route $route, array $tags, array $routeProps)
{
return $this->getTransformerResponse($tags);
}
Expand Down
6 changes: 3 additions & 3 deletions tests/Unit/GeneratorTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
namespace Mpociot\ApiDoc\Tests\Unit;

use Orchestra\Testbench\TestCase;
use Mpociot\ApiDoc\Generators\Generator;
use Mpociot\ApiDoc\Tools\Generator;
use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider;

abstract class GeneratorTestCase extends TestCase
{
/**
* @var \Mpociot\ApiDoc\Generators\Generator
* @var \Mpociot\ApiDoc\Tools\Generator
*/
protected $generator;

Expand Down Expand Up @@ -228,7 +228,7 @@ public function can_parse_transformer_collection_tag_with_model()
/** @test */
public function can_call_route_and_generate_response()
{
$route = $this->createRoute('PUT', '/shouldFetchRouteResponse', 'shouldFetchRouteResponse', true);
$route = $this->createRoute('POST', '/shouldFetchRouteResponse', 'shouldFetchRouteResponse', true);

$rules = [
'response_calls' => [
Expand Down