From 3339c64e6882a40eca95c16ea05a8603cfb4d5de Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 22 Apr 2020 23:09:11 -0400 Subject: [PATCH 01/36] comment cleanup --- src/Lib/Annotation/SwagEntity.php | 1 + src/Lib/Annotation/SwagEntityAttribute.php | 9 +++++++++ src/Lib/Annotation/SwagForm.php | 7 ++++++- src/Lib/Annotation/SwagHeader.php | 7 ++++++- src/Lib/Annotation/SwagOperation.php | 1 + src/Lib/Annotation/SwagPath.php | 1 + src/Lib/Annotation/SwagQuery.php | 7 ++++++- src/Lib/Annotation/SwagRequestBody.php | 5 +++++ src/Lib/Annotation/SwagRequestBodyContent.php | 5 +++++ src/Lib/Annotation/SwagResponseSchema.php | 5 +++++ src/Lib/Annotation/SwagSecurity.php | 3 +++ 11 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/Lib/Annotation/SwagEntity.php b/src/Lib/Annotation/SwagEntity.php index 4ec8f2e9..00bf514e 100644 --- a/src/Lib/Annotation/SwagEntity.php +++ b/src/Lib/Annotation/SwagEntity.php @@ -13,6 +13,7 @@ */ class SwagEntity { + /** @var bool **/ public $isVisible; public function __construct(array $values) diff --git a/src/Lib/Annotation/SwagEntityAttribute.php b/src/Lib/Annotation/SwagEntityAttribute.php index f4c9a41b..695ee71c 100644 --- a/src/Lib/Annotation/SwagEntityAttribute.php +++ b/src/Lib/Annotation/SwagEntityAttribute.php @@ -17,10 +17,19 @@ */ class SwagEntityAttribute { + /** @var string */ public $name; + + /** @var string */ public $type; + + /** @var bool */ public $readOnly; + + /** @var bool */ public $writeOnly; + + /** @var bool */ public $required; public function __construct(array $values) diff --git a/src/Lib/Annotation/SwagForm.php b/src/Lib/Annotation/SwagForm.php index 33f146de..fa404c23 100644 --- a/src/Lib/Annotation/SwagForm.php +++ b/src/Lib/Annotation/SwagForm.php @@ -17,8 +17,13 @@ */ class SwagForm { + /** @var string */ public $name; + + /** @var string */ public $type; + + /** @var bool */ public $required; public function __construct(array $values) @@ -42,6 +47,6 @@ public function __construct(array $values) $this->name = $values['name']; $this->type = $values['type']; - $this->required = $values['required']; + $this->required = (bool) $values['required']; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagHeader.php b/src/Lib/Annotation/SwagHeader.php index b242ce51..20de69f4 100644 --- a/src/Lib/Annotation/SwagHeader.php +++ b/src/Lib/Annotation/SwagHeader.php @@ -15,8 +15,13 @@ */ class SwagHeader { + /** @var string */ public $name; + + /** @var string */ public $type; + + /** @var bool */ public $required; public function __construct(array $values) @@ -29,6 +34,6 @@ public function __construct(array $values) $this->name = $values['name']; $this->type = $values['type']; - $this->required = $values['required']; + $this->required = (bool) $values['required']; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagOperation.php b/src/Lib/Annotation/SwagOperation.php index 568a4563..251dada4 100644 --- a/src/Lib/Annotation/SwagOperation.php +++ b/src/Lib/Annotation/SwagOperation.php @@ -13,6 +13,7 @@ */ class SwagOperation { + /** @var bool */ public $isVisible; public function __construct(array $values) diff --git a/src/Lib/Annotation/SwagPath.php b/src/Lib/Annotation/SwagPath.php index 0b36dd97..7ab93281 100644 --- a/src/Lib/Annotation/SwagPath.php +++ b/src/Lib/Annotation/SwagPath.php @@ -13,6 +13,7 @@ */ class SwagPath { + /** @var bool */ public $isVisible; public function __construct(array $values) diff --git a/src/Lib/Annotation/SwagQuery.php b/src/Lib/Annotation/SwagQuery.php index c9f9cc5e..34a0b305 100644 --- a/src/Lib/Annotation/SwagQuery.php +++ b/src/Lib/Annotation/SwagQuery.php @@ -17,8 +17,13 @@ */ class SwagQuery { + /** @var string */ public $name; + + /** @var string */ public $type; + + /** @var bool */ public $required; public function __construct(array $values) @@ -40,6 +45,6 @@ public function __construct(array $values) $this->name = $values['name']; $this->type = $values['type']; - $this->required = $values['required']; + $this->required = (bool) $values['required']; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagRequestBody.php b/src/Lib/Annotation/SwagRequestBody.php index f0db8d64..4e3ea667 100644 --- a/src/Lib/Annotation/SwagRequestBody.php +++ b/src/Lib/Annotation/SwagRequestBody.php @@ -15,8 +15,13 @@ */ class SwagRequestBody { + /** @var string */ public $description; + + /** @var bool */ public $required; + + /** @var bool */ public $ignoreCakeSchema; public function __construct(array $values) diff --git a/src/Lib/Annotation/SwagRequestBodyContent.php b/src/Lib/Annotation/SwagRequestBodyContent.php index 1d43244c..bf115343 100644 --- a/src/Lib/Annotation/SwagRequestBodyContent.php +++ b/src/Lib/Annotation/SwagRequestBodyContent.php @@ -14,8 +14,13 @@ */ class SwagRequestBodyContent { + /** @var string */ public $refEntity; + + /** @var string */ public $mimeType; + + /** @var bool */ public $ignoreCakeSchema; public function __construct(array $values) diff --git a/src/Lib/Annotation/SwagResponseSchema.php b/src/Lib/Annotation/SwagResponseSchema.php index fe7a034a..5e705866 100644 --- a/src/Lib/Annotation/SwagResponseSchema.php +++ b/src/Lib/Annotation/SwagResponseSchema.php @@ -15,8 +15,13 @@ */ class SwagResponseSchema { + /** @var string */ public $refEntity; + + /** @var int */ public $httpCode = 200; + + /** @var string */ public $description; public function __construct(array $values) diff --git a/src/Lib/Annotation/SwagSecurity.php b/src/Lib/Annotation/SwagSecurity.php index 8eea45be..793dc1b5 100644 --- a/src/Lib/Annotation/SwagSecurity.php +++ b/src/Lib/Annotation/SwagSecurity.php @@ -14,7 +14,10 @@ */ class SwagSecurity { + /** @var string */ public $name; + + /** @var string */ public $scopes; public function __construct(array $values) From d7120ad8962a27dd87b74a03cdb9f54623882337 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 22 Apr 2020 23:18:55 -0400 Subject: [PATCH 02/36] remove extra return line --- src/Lib/Swagger.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index 236a7ca8..5e024624 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -235,7 +235,6 @@ private function pathWithResponses(Path $path) : Path foreach ($path->getTags() as $tag) { $className = Inflector::classify($tag); - if (!$path->getResponseByCode(200) && $this->getSchemaByName($className)) { $response = new Response(); $response From acb03d00ac7d76c165021080e8dd7ec0c24f77f3 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 22 Apr 2020 23:21:05 -0400 Subject: [PATCH 03/36] remove extra return line --- src/Lib/Factory/PathFactory.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Lib/Factory/PathFactory.php b/src/Lib/Factory/PathFactory.php index fa8b2587..d18457b5 100644 --- a/src/Lib/Factory/PathFactory.php +++ b/src/Lib/Factory/PathFactory.php @@ -171,7 +171,6 @@ private function withResponses(Path $path, array $annotations) : Path ); } - return $path; } From e3f88dcb835dbc95594686e371ad4d451b32b502 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 23 Apr 2020 00:29:21 -0400 Subject: [PATCH 04/36] Limit display of bin/cake swagger routes - Ignore routes without RESTful methods - Ignore DebugKit plugin - Improve error messaging on bin commands --- src/Command/ModelCommand.php | 10 ++++++-- src/Command/RouteCommand.php | 10 +++++--- src/Lib/CakeRoute.php | 46 +++++++++++++++++++++++++++++------- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/Command/ModelCommand.php b/src/Command/ModelCommand.php index 305d4404..8400df3d 100644 --- a/src/Command/ModelCommand.php +++ b/src/Command/ModelCommand.php @@ -25,7 +25,9 @@ class ModelCommand extends Command */ public function execute(Arguments $args, ConsoleIo $io) { - $io->out("Running..."); + $io->hr(); + $io->out("| SwaggerBake is checking your models..."); + $io->hr(); ValidateConfiguration::validate(); @@ -36,7 +38,11 @@ public function execute(Arguments $args, ConsoleIo $io) $models = $cakeModel->getModels(); if (empty($models)) { - return $io->warning('No models found'); + $io->out(); + $io->warning('No models were found that are associated with: ' . $config->getPrefix()); + $io->out("Have you added RESTful routes? Do you have models associated with those routes?"); + $io->out(); + return; } $header = ['Attribute','Data Type', 'Swagger Type','Default','Primary Key']; diff --git a/src/Command/RouteCommand.php b/src/Command/RouteCommand.php index e8d620bf..584532ba 100644 --- a/src/Command/RouteCommand.php +++ b/src/Command/RouteCommand.php @@ -25,7 +25,9 @@ class RouteCommand extends Command */ public function execute(Arguments $args, ConsoleIo $io) { - $io->out("Running..."); + $io->hr(); + $io->out("| SwaggerBake is checking your routes..."); + $io->hr(); ValidateConfiguration::validate(); @@ -39,8 +41,10 @@ public function execute(Arguments $args, ConsoleIo $io) $routes = $cakeRoute->getRoutes(); if (empty($routes)) { - $io->out("No routes were found for: $prefix"); - $io->out('https://book.cakephp.org/4/en/development/routing.html#restful-routing'); + $io->out(); + $io->warning("No routes were found for: $prefix"); + $io->out("Have you added RESTful routes? Do you have models associated with those routes?"); + $io->out(); return; } diff --git a/src/Lib/CakeRoute.php b/src/Lib/CakeRoute.php index d252a619..09074e5d 100644 --- a/src/Lib/CakeRoute.php +++ b/src/Lib/CakeRoute.php @@ -14,13 +14,25 @@ */ class CakeRoute { + private const EXCLUDED_PLUGINS = [ + 'DebugKit' + ]; + + /** @var Router */ private $router; + + /** @var string */ private $prefix; + /** @var int */ + private $prefixLength = 0; + public function __construct(Router $router, Configuration $config) { $this->router = $router; $this->prefix = $config->getPrefix(); + $this->prefixLength = strlen($this->prefix); + } public function getRoutes() : array @@ -29,16 +41,10 @@ public function getRoutes() : array throw new InvalidArgumentException('route prefix is invalid'); } - $length = strlen($this->prefix); + $routes = $this->router::routes(); - return array_filter($this->router::routes(), function ($route) use ($length) { - if (substr($route->template, 0, $length) != $this->prefix) { - return null; - } - if (substr($route->template, $length) == '') { - return null; - } - return true; + return array_filter($routes, function ($route) { + return $this->isRouteAllowed($route); }); } @@ -52,4 +58,26 @@ public function getControllerFromRoute(Route $route) : ?string return $defaults['controller']; } + + private function isRouteAllowed(Route $route) : bool + { + if (substr($route->template, 0, $this->prefixLength) != $this->prefix) { + return false; + } + if (substr($route->template, $this->prefixLength) == '') { + return false; + } + + $defaults = (array) $route->defaults; + + if (!isset($defaults['_method']) || empty($defaults['_method'])) { + return false; + } + + if (isset($defaults['plugin']) && in_array($defaults['plugin'], self::EXCLUDED_PLUGINS)) { + return false; + } + + return true; + } } From 23e56fe7270f07cdeb4979f99dff16522c9a0e2e Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 23 Apr 2020 00:30:50 -0400 Subject: [PATCH 05/36] type comments --- src/Lib/CakeModel.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Lib/CakeModel.php b/src/Lib/CakeModel.php index ebabf374..11ee77b3 100644 --- a/src/Lib/CakeModel.php +++ b/src/Lib/CakeModel.php @@ -18,8 +18,13 @@ */ class CakeModel { + /** @var CakeRoute */ private $cakeRoute; + + /** @var string */ private $prefix; + + /** @var Configuration */ private $config; public function __construct(CakeRoute $cakeRoute, Configuration $config) From ad27cc4af21aa17c5680da82458c64b85508ecb7 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 23 Apr 2020 00:31:44 -0400 Subject: [PATCH 06/36] added comments --- src/Lib/CakeModel.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Lib/CakeModel.php b/src/Lib/CakeModel.php index 11ee77b3..8eb0207b 100644 --- a/src/Lib/CakeModel.php +++ b/src/Lib/CakeModel.php @@ -34,6 +34,11 @@ public function __construct(CakeRoute $cakeRoute, Configuration $config) $this->config = $config; } + /** + * Gets an array of ExpressiveModel + * + * @return array + */ public function getModels() : array { $return = []; From 80187e803bdc7098bebd74e8d430fa9f1f2b4fcc Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 23 Apr 2020 00:32:56 -0400 Subject: [PATCH 07/36] more comments --- src/Lib/CakeModel.php | 2 +- src/Lib/CakeRoute.php | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Lib/CakeModel.php b/src/Lib/CakeModel.php index 8eb0207b..2e20913e 100644 --- a/src/Lib/CakeModel.php +++ b/src/Lib/CakeModel.php @@ -35,7 +35,7 @@ public function __construct(CakeRoute $cakeRoute, Configuration $config) } /** - * Gets an array of ExpressiveModel + * Gets an array of ExpressiveModel objects * * @return array */ diff --git a/src/Lib/CakeRoute.php b/src/Lib/CakeRoute.php index 09074e5d..cab2f50e 100644 --- a/src/Lib/CakeRoute.php +++ b/src/Lib/CakeRoute.php @@ -35,6 +35,10 @@ public function __construct(Router $router, Configuration $config) } + /** + * Gets an array of Route objects + * @return array + */ public function getRoutes() : array { if (empty($this->prefix) || !filter_var('http://foo.com' . $this->prefix, FILTER_VALIDATE_URL)) { @@ -48,6 +52,12 @@ public function getRoutes() : array }); } + /** + * Returns controller name from the given Route argument + * + * @param Route $route + * @return string|null + */ public function getControllerFromRoute(Route $route) : ?string { $defaults = (array) $route->defaults; From c820e48426e06cf40676df2aeb7e67b98b4f8ace Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 23 Apr 2020 00:33:17 -0400 Subject: [PATCH 08/36] comment spacing --- src/Lib/CakeRoute.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Lib/CakeRoute.php b/src/Lib/CakeRoute.php index cab2f50e..2653ab45 100644 --- a/src/Lib/CakeRoute.php +++ b/src/Lib/CakeRoute.php @@ -37,6 +37,7 @@ public function __construct(Router $router, Configuration $config) /** * Gets an array of Route objects + * * @return array */ public function getRoutes() : array From 31c8e31492890406e98814e3299247d0d410bedf Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 23 Apr 2020 00:35:27 -0400 Subject: [PATCH 09/36] comments --- src/Lib/CakeModel.php | 4 ++-- src/Lib/CakeRoute.php | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Lib/CakeModel.php b/src/Lib/CakeModel.php index 2e20913e..e12bbe8f 100644 --- a/src/Lib/CakeModel.php +++ b/src/Lib/CakeModel.php @@ -35,9 +35,9 @@ public function __construct(CakeRoute $cakeRoute, Configuration $config) } /** - * Gets an array of ExpressiveModel objects + * Gets an array of ExpressiveModel * - * @return array + * @return ExpressiveModel[] */ public function getModels() : array { diff --git a/src/Lib/CakeRoute.php b/src/Lib/CakeRoute.php index 2653ab45..fc7cc674 100644 --- a/src/Lib/CakeRoute.php +++ b/src/Lib/CakeRoute.php @@ -14,6 +14,7 @@ */ class CakeRoute { + /** @var string[] */ private const EXCLUDED_PLUGINS = [ 'DebugKit' ]; @@ -36,9 +37,9 @@ public function __construct(Router $router, Configuration $config) } /** - * Gets an array of Route objects + * Gets an array of Route * - * @return array + * @return Route[] */ public function getRoutes() : array { @@ -54,7 +55,7 @@ public function getRoutes() : array } /** - * Returns controller name from the given Route argument + * Returns controller name from the Route argument * * @param Route $route * @return string|null From 2eca0b39ce124963efcf12b0ec7fcf1923af7d66 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 23 Apr 2020 21:22:44 -0400 Subject: [PATCH 10/36] Refactored routes - Created ExpressRoute for better describing cake routes - Improved layout of information in swagger routes command --- src/Command/RouteCommand.php | 13 +++-- src/Lib/AbstractParameter.php | 9 ++-- src/Lib/CakeModel.php | 5 +- src/Lib/CakeRoute.php | 42 +++++++++------- src/Lib/Factory/PathFactory.php | 30 +++++------ src/Lib/Model/ExpressiveRoute.php | 83 +++++++++++++++++++++++++++++++ src/Lib/RequestBodyBuilder.php | 5 +- src/Lib/Security.php | 1 - src/Lib/Swagger.php | 8 +-- 9 files changed, 144 insertions(+), 52 deletions(-) create mode 100644 src/Lib/Model/ExpressiveRoute.php diff --git a/src/Command/RouteCommand.php b/src/Command/RouteCommand.php index 584532ba..70131d3b 100644 --- a/src/Command/RouteCommand.php +++ b/src/Command/RouteCommand.php @@ -32,7 +32,7 @@ public function execute(Arguments $args, ConsoleIo $io) ValidateConfiguration::validate(); $output = [ - ['Route name', 'URI template', 'Defaults'], + ['Route name', 'URI template', 'Method(s)', 'Controller', 'Action', 'Plugin'], ]; $config = new Configuration(); @@ -49,9 +49,14 @@ public function execute(Arguments $args, ConsoleIo $io) } foreach ($routes as $route) { - $name = $route->options['_name'] ?? $route->getName(); - ksort($route->defaults); - $output[] = [$name, $route->template, json_encode($route->defaults)]; + $output[] = [ + $route->getName(), + $route->getTemplate(), + implode(', ', $route->getMethods()), + $route->getController(), + $route->getAction(), + $route->getPlugin(), + ]; } $io->helper('table')->output($output); diff --git a/src/Lib/AbstractParameter.php b/src/Lib/AbstractParameter.php index 8bbb3b1e..e2d86531 100644 --- a/src/Lib/AbstractParameter.php +++ b/src/Lib/AbstractParameter.php @@ -2,10 +2,10 @@ namespace SwaggerBake\Lib; -use Cake\Routing\Route\Route; use Doctrine\Common\Annotations\AnnotationReader; use ReflectionClass; use SwaggerBake\Lib\Exception\SwaggerBakeRunTimeException; +use SwaggerBake\Lib\Model\ExpressiveRoute; class AbstractParameter { @@ -18,14 +18,13 @@ class AbstractParameter protected $reflectionMethods; protected $config; - public function __construct(Route $route, Configuration $config) + public function __construct(ExpressiveRoute $route, Configuration $config) { $this->config = $config; $this->route = $route; - $defaults = (array) $this->route->defaults; - $this->actionName = $defaults['action']; - $this->className = $defaults['controller'] . 'Controller'; + $this->actionName = $this->route->getAction(); + $this->className = $this->route->getController() . 'Controller'; $this->controller = $this->getControllerFromNamespaces($this->className); $instance = new $this->controller; diff --git a/src/Lib/CakeModel.php b/src/Lib/CakeModel.php index e12bbe8f..fcc2a794 100644 --- a/src/Lib/CakeModel.php +++ b/src/Lib/CakeModel.php @@ -115,11 +115,10 @@ private function getTablesFromRoutes(array $routes) : array { $return = []; foreach ($routes as $route) { - $controllerName = $this->cakeRoute->getControllerFromRoute($route); - if (empty($controllerName)) { + if (empty($route->getController())) { continue; } - $return[] = Inflector::underscore($controllerName); + $return[] = Inflector::underscore($route->getController()); } return array_unique($return); } diff --git a/src/Lib/CakeRoute.php b/src/Lib/CakeRoute.php index fc7cc674..dfc15696 100644 --- a/src/Lib/CakeRoute.php +++ b/src/Lib/CakeRoute.php @@ -1,9 +1,8 @@ router = $router; $this->prefix = $config->getPrefix(); $this->prefixLength = strlen($this->prefix); - } /** * Gets an array of Route * - * @return Route[] + * @return ExpressiveRoute[] */ public function getRoutes() : array { @@ -47,28 +45,38 @@ public function getRoutes() : array throw new InvalidArgumentException('route prefix is invalid'); } - $routes = $this->router::routes(); - - return array_filter($routes, function ($route) { + $filteredRoutes = array_filter($this->router::routes(), function ($route) { return $this->isRouteAllowed($route); }); + + $routes = []; + + foreach ($filteredRoutes as $route) { + $routes[$route->getName()] = $this->createExpressiveRouteFromRoute($route); + } + + ksort($routes); + + return $routes; } - /** - * Returns controller name from the Route argument - * - * @param Route $route - * @return string|null - */ - public function getControllerFromRoute(Route $route) : ?string + private function createExpressiveRouteFromRoute(Route $route) : ExpressiveRoute { $defaults = (array) $route->defaults; - if (!isset($defaults['controller'])) { - return null; + $methods = $defaults['_method']; + if (!is_array($defaults['_method'])) { + $methods = explode(', ', $defaults['_method']); } - return $defaults['controller']; + return (new ExpressiveRoute()) + ->setPlugin($defaults['plugin']) + ->setController($defaults['controller']) + ->setName($route->getName()) + ->setAction($defaults['action']) + ->setMethods($methods) + ->setTemplate($route->template) + ; } private function isRouteAllowed(Route $route) : bool diff --git a/src/Lib/Factory/PathFactory.php b/src/Lib/Factory/PathFactory.php index d18457b5..38c1bedd 100644 --- a/src/Lib/Factory/PathFactory.php +++ b/src/Lib/Factory/PathFactory.php @@ -2,7 +2,6 @@ namespace SwaggerBake\Lib\Factory; -use Cake\Routing\Route\Route; use Cake\Utility\Inflector; use Exception; use phpDocumentor\Reflection\DocBlock; @@ -12,6 +11,7 @@ use SwaggerBake\Lib\Configuration; use SwaggerBake\Lib\Exception\SwaggerBakeRunTimeException; use SwaggerBake\Lib\ExceptionHandler; +use SwaggerBake\Lib\Model\ExpressiveRoute; use SwaggerBake\Lib\OpenApi\OperationExternalDoc; use SwaggerBake\Lib\OpenApi\Path; use SwaggerBake\Lib\OpenApi\Parameter; @@ -28,7 +28,7 @@ class PathFactory private $prefix = ''; private $config; - public function __construct(Route $route, Configuration $config) + public function __construct(ExpressiveRoute $route, Configuration $config) { $this->config = $config; $this->route = $route; @@ -44,19 +44,21 @@ public function __construct(Route $route, Configuration $config) public function create() : ?Path { $path = new Path(); - $defaults = (array) $this->route->defaults; - if (empty($defaults['_method'])) { + if (empty($this->route->getMethods())) { return null; } - if (!$this->isControllerVisible($defaults['controller'])) { + if (!$this->isControllerVisible($this->route->getController())) { return null; } - foreach ((array) $defaults['_method'] as $method) { + foreach ($this->route->getMethods() as $method) { - $methodAnnotations = $this->getMethodAnnotations($defaults['controller'], $defaults['action']); + $methodAnnotations = $this->getMethodAnnotations( + $this->route->getController(), + $this->route->getAction() + ); if (!$this->isMethodVisible($methodAnnotations)) { continue; @@ -69,7 +71,7 @@ public function create() : ?Path ->setSummary($this->dockBlock ? $this->dockBlock->getSummary() : '') ->setDescription($this->dockBlock ? $this->dockBlock->getDescription() : '') ->setTags([ - Inflector::humanize(Inflector::underscore($defaults['controller'])) + Inflector::humanize(Inflector::underscore($this->route->getController())) ]) ->setParameters($this->getPathParameters()) ->setDeprecated($this->isDeprecated()) @@ -92,7 +94,7 @@ function ($piece) { } return $piece; }, - explode('/', $this->route->template) + explode('/', $this->route->getTemplate()) ); $length = strlen($this->prefix); @@ -104,7 +106,7 @@ private function getPathParameters() : array { $return = []; - $pieces = explode('/', $this->route->template); + $pieces = explode('/', $this->route->getTemplate()); $results = array_filter($pieces, function ($piece) { return substr($piece, 0, 1) == ':' ? true : null; }); @@ -207,14 +209,12 @@ private function withRequestBody(Path $path, array $annotations) : Path private function getDocBlock() : ?DocBlock { - $defaults = (array) $this->route->defaults; - - if (!isset($defaults['controller'])) { + if (empty($this->route->getController())) { return null; } - $className = $defaults['controller'] . 'Controller'; - $methodName = $defaults['action']; + $className = $this->route->getController() . 'Controller'; + $methodName = $this->route->getAction(); $controller = $this->getControllerFromNamespaces($className); diff --git a/src/Lib/Model/ExpressiveRoute.php b/src/Lib/Model/ExpressiveRoute.php new file mode 100644 index 00000000..edb80f31 --- /dev/null +++ b/src/Lib/Model/ExpressiveRoute.php @@ -0,0 +1,83 @@ +name; + } + + public function setName($name) + { + $this->name = $name; + return $this; + } + + public function getPlugin() + { + return $this->plugin; + } + + public function setPlugin($plugin) + { + $this->plugin = $plugin; + return $this; + } + + public function getController() + { + return $this->controller; + } + + public function setController($controller) + { + $this->controller = $controller; + return $this; + } + + public function getAction() + { + return $this->action; + } + + public function setAction($action) + { + $this->action = $action; + return $this; + } + + public function getMethods(): array + { + return $this->methods; + } + + public function setMethods(array $methods): ExpressiveRoute + { + $this->methods = $methods; + return $this; + } + + public function getTemplate() + { + return $this->template; + } + + public function setTemplate($template) + { + $this->template = $template; + return $this; + } +} \ No newline at end of file diff --git a/src/Lib/RequestBodyBuilder.php b/src/Lib/RequestBodyBuilder.php index cb183b2d..0e33fbe3 100644 --- a/src/Lib/RequestBodyBuilder.php +++ b/src/Lib/RequestBodyBuilder.php @@ -3,17 +3,16 @@ namespace SwaggerBake\Lib; -use Cake\Routing\Route\Route; use Cake\Utility\Inflector; +use SwaggerBake\Lib\Model\ExpressiveRoute; use SwaggerBake\Lib\OpenApi\Content; use SwaggerBake\Lib\OpenApi\Path; use SwaggerBake\Lib\OpenApi\RequestBody; use SwaggerBake\Lib\OpenApi\Schema; -use SwaggerBake\Lib\OpenApi\SchemaProperty; class RequestBodyBuilder { - public function __construct(Path $path, Swagger $swagger, Route $route) + public function __construct(Path $path, Swagger $swagger, ExpressiveRoute $route) { $this->path = $path; $this->route = $route; diff --git a/src/Lib/Security.php b/src/Lib/Security.php index f0860245..0bf9538a 100644 --- a/src/Lib/Security.php +++ b/src/Lib/Security.php @@ -2,7 +2,6 @@ namespace SwaggerBake\Lib; - use SwaggerBake\Lib\Annotation as SwagAnnotation; class Security extends AbstractParameter diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index 5e024624..d76e7035 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -2,10 +2,10 @@ namespace SwaggerBake\Lib; -use Cake\Routing\Route\Route; use Cake\Utility\Inflector; use SwaggerBake\Lib\Exception\SwaggerBakeRunTimeException; use SwaggerBake\Lib\Factory as Factory; +use SwaggerBake\Lib\Model\ExpressiveRoute; use SwaggerBake\Lib\OpenApi\Path; use SwaggerBake\Lib\OpenApi\Response; use SwaggerBake\Lib\OpenApi\Schema; @@ -210,13 +210,13 @@ private function buildPaths(): void } } - private function pathWithSecurity(Path $path, Route $route) : Path + private function pathWithSecurity(Path $path, ExpressiveRoute $route) : Path { $path->setSecurity((new Security($route, $this->config))->getPathSecurity()); return $path; } - private function pathWithParameters(Path $path, Route $route) : Path + private function pathWithParameters(Path $path, ExpressiveRoute $route) : Path { $headers = (new HeaderParameter($route, $this->config))->getHeaderParameters(); foreach ($headers as $parameter) { @@ -251,7 +251,7 @@ private function pathWithResponses(Path $path) : Path return $path; } - private function pathWithRequestBody(Path $path, Route $route) : Path + private function pathWithRequestBody(Path $path, ExpressiveRoute $route) : Path { $requestBody = (new RequestBodyBuilder($path, $this, $route))->build(); if ($requestBody) { From 6c518ee121bda023d158dbb43ac0d6524dca5abb Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 23 Apr 2020 22:29:18 -0400 Subject: [PATCH 11/36] Do not set HTTP 200 if path has 20x response If the path already has a HTTP 20x response defined, then do not add the default 200 response code. --- src/Lib/OpenApi/Path.php | 13 +++++++++++-- src/Lib/Swagger.php | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Lib/OpenApi/Path.php b/src/Lib/OpenApi/Path.php index dacedce9..814b0e0e 100644 --- a/src/Lib/OpenApi/Path.php +++ b/src/Lib/OpenApi/Path.php @@ -26,13 +26,13 @@ class Path private $security = []; private $deprecated = false; - public function toArray() : array + public function toArray(): array { $vars = get_object_vars($this); unset($vars['type']); unset($vars['path']); - if (in_array($this->type, ['get','delete'])) { + if (in_array($this->type, ['get', 'delete'])) { unset($vars['requestBody']); } if (empty($vars['security'])) { @@ -45,6 +45,15 @@ public function toArray() : array return $vars; } + public function hasSuccessResponseCode() : bool + { + $results = array_filter($this->getResponses(), function ($response) { + return ($response->getCode() >= 200 && $response->getCode() < 300); + }); + + return count($results) > 0; + } + /** * @return string */ diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index d76e7035..7b143f56 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -235,7 +235,7 @@ private function pathWithResponses(Path $path) : Path foreach ($path->getTags() as $tag) { $className = Inflector::classify($tag); - if (!$path->getResponseByCode(200) && $this->getSchemaByName($className)) { + if (!$path->hasSuccessResponseCode() && $this->getSchemaByName($className)) { $response = new Response(); $response ->setSchemaRef('#/components/schemas/' . $className) @@ -244,7 +244,7 @@ private function pathWithResponses(Path $path) : Path } } - if (!$path->getResponseByCode(200)) { + if (!$path->hasSuccessResponseCode()) { $path->pushResponse((new Response())->setCode(200)); } From 17f780be0d2f581568fd6dd04ea5a5a03d21d7bd Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Fri, 24 Apr 2020 00:00:10 -0400 Subject: [PATCH 12/36] Fix APIs set to prefix of forward-slash "/" only Swagger HTTP requests were not working if the prefix was set to just "/". This was attempting to HTTP using http://localhost:8765actors instead of http://localhost:8765/actors --- src/Lib/Factory/PathFactory.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Lib/Factory/PathFactory.php b/src/Lib/Factory/PathFactory.php index 38c1bedd..8360e26f 100644 --- a/src/Lib/Factory/PathFactory.php +++ b/src/Lib/Factory/PathFactory.php @@ -97,9 +97,14 @@ function ($piece) { explode('/', $this->route->getTemplate()) ); - $length = strlen($this->prefix); + if ($this->prefix == '/') { + return implode('/', $pieces); + } - return substr(implode('/', $pieces), $length); + return substr( + implode('/', $pieces), + strlen($this->prefix) + ); } private function getPathParameters() : array From eed88f1025ee57de528e8f02b675d3ff1f6fc224 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 19:35:31 -0400 Subject: [PATCH 13/36] Note on missing CSRF token body --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 446c9ff8..bbd2a1b3 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,11 @@ paths using CakePHPs route resource functionality. Read the [Cake Routing documentation](https://book.cakephp.org/4/en/development/routing.html) 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. + ## Reporting Issues This is a new library so please take some steps before reporting issues. You can copy & paste the JSON SwaggerBake From 12b2cdf682e96ebf9f845fa0b559e149727505b4 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 19:51:45 -0400 Subject: [PATCH 14/36] Type hint array of Response for getResponses() --- src/Lib/OpenApi/Path.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lib/OpenApi/Path.php b/src/Lib/OpenApi/Path.php index 814b0e0e..058cc948 100644 --- a/src/Lib/OpenApi/Path.php +++ b/src/Lib/OpenApi/Path.php @@ -196,7 +196,7 @@ public function setRequestBody(RequestBody $requestBody) : Path } /** - * @return array + * @return Response[] */ public function getResponses(): array { From 19a6fb86cb723f1194cf108d3c774dcfbe4bb690 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 20:12:02 -0400 Subject: [PATCH 15/36] Added support for an Exception schema. - Added support for Schema Property example attribute - New exceptionSchema config in case user wants to name their exception other than Exception - Read schema in from default swagger.yml as SwaggerBake objects --- assets/swagger.yml | 15 ++++++++ assets/swagger_bake.php | 1 + src/Lib/Configuration.php | 6 ++++ src/Lib/OpenApi/SchemaProperty.php | 32 +++++++++++++++++ src/Lib/Swagger.php | 57 ++++++++++++++++++++++++------ 5 files changed, 101 insertions(+), 10 deletions(-) diff --git a/assets/swagger.yml b/assets/swagger.yml index 94ca3730..ad5d65dc 100644 --- a/assets/swagger.yml +++ b/assets/swagger.yml @@ -9,3 +9,18 @@ servers: paths: definitions: + +components: + schemas: + Exception: + type: object + properties: + code: + type: integer + example: 500 + url: + type: string + example: /url/path + message: + type: string + example: Internal Error diff --git a/assets/swagger_bake.php b/assets/swagger_bake.php index b13e3759..a9dd0ea9 100644 --- a/assets/swagger_bake.php +++ b/assets/swagger_bake.php @@ -23,6 +23,7 @@ 'webPath' => '/swagger.json', 'hotReload' => false, 'docType' => 'swagger', + 'exceptionSchema' => 'Exception', 'namespaces' => [ 'controllers' => ['\App\\'], 'entities' => ['\App\\'], diff --git a/src/Lib/Configuration.php b/src/Lib/Configuration.php index ff22473e..f71728bf 100644 --- a/src/Lib/Configuration.php +++ b/src/Lib/Configuration.php @@ -24,6 +24,7 @@ public function __construct($config = [], $root = ROOT) [ 'docType' => 'swagger', 'hotReload' => false, + 'exceptionSchema' => 'Exception', 'namespaces' => [ 'controllers' => ['\App\\'], 'entities' => ['\App\\'], @@ -89,6 +90,11 @@ public function getDocType() : string return strtolower($this->get('docType')); } + public function getExceptionSchema() : string + { + return $this->get('exceptionSchema'); + } + public function getLayout(?string $doctype = null) : string { $doctype = empty($doctype) ? $this->getDocType() : $doctype; diff --git a/src/Lib/OpenApi/SchemaProperty.php b/src/Lib/OpenApi/SchemaProperty.php index 4bfdef38..bba18f19 100644 --- a/src/Lib/OpenApi/SchemaProperty.php +++ b/src/Lib/OpenApi/SchemaProperty.php @@ -7,11 +7,25 @@ class SchemaProperty implements JsonSerializable { + /** @var string */ private $name = ''; + + /** @var string */ private $type = ''; + + /** @var string */ private $format = ''; + + /** @var string */ + private $example = ''; + + /** @var bool */ private $readOnly = false; + + /** @var bool */ private $writeOnly = false; + + /** @var bool */ private $required = false; public function toArray() : array @@ -134,4 +148,22 @@ public function setRequired(bool $required): SchemaProperty $this->required = $required; return $this; } + + /** + * @return string + */ + public function getExample(): string + { + return $this->example; + } + + /** + * @param string $example + * @return SchemaProperty + */ + public function setExample(string $example): SchemaProperty + { + $this->example = $example; + return $this; + } } \ No newline at end of file diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index 7b143f56..3565fa0a 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -9,6 +9,7 @@ use SwaggerBake\Lib\OpenApi\Path; use SwaggerBake\Lib\OpenApi\Response; use SwaggerBake\Lib\OpenApi\Schema; +use SwaggerBake\Lib\OpenApi\SchemaProperty; use Symfony\Component\Yaml\Yaml; class Swagger @@ -23,16 +24,7 @@ public function __construct(CakeModel $cakeModel) $this->cakeModel = $cakeModel; $this->cakeRoute = $cakeModel->getCakeRoute(); $this->config = $cakeModel->getConfig(); - - $array = Yaml::parseFile($this->config->getYml()); - if (!isset($array['paths'])) { - $array['paths'] = []; - } - if (!isset($array['components']['schemas'])) { - $array['components']['schemas'] = []; - } - - $this->array = $array; + $this->buildFromDefaults(); } /** @@ -248,6 +240,20 @@ private function pathWithResponses(Path $path) : Path $path->pushResponse((new Response())->setCode(200)); } + $exceptionSchema = $this->getSchemaByName($this->getConfig()->getExceptionSchema()); + if (!$exceptionSchema) { + return $path; + } + + foreach ($path->getResponses() as $response) { + if ($response->getCode() < 400) { + continue; + } + $path->pushResponse( + $response->setSchemaRef('#/components/schemas/' . $exceptionSchema->getName()) + ); + } + return $path; } @@ -260,6 +266,37 @@ private function pathWithRequestBody(Path $path, ExpressiveRoute $route) : Path return $path; } + private function buildFromDefaults() + { + $array = Yaml::parseFile($this->config->getYml()); + if (!isset($array['paths'])) { + $array['paths'] = []; + } + if (!isset($array['components']['schemas'])) { + $array['components']['schemas'] = []; + } + + foreach ($array['components']['schemas'] as $schemaName => $schemaVar) { + $schema = (new Schema()) + ->setName($schemaName) + ->setType($schemaVar['type']); + + foreach ($schemaVar['properties'] as $propertyName => $propertyVar) { + $property = (new SchemaProperty()) + ->setType($propertyVar['type']) + ->setName($propertyName) + ->setFormat($propertyVar['type'] ?? '') + ->setExample($propertyVar['example'] ?? '') + ; + $schema->pushProperty($property); + } + + $array['components']['schemas'][$schemaName] = $schema; + } + + $this->array = $array; + } + public function __toString(): string { return $this->toString(); From 8b124ce4af4fe9058f6333b1a9a6335ac7bdba66 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 20:15:39 -0400 Subject: [PATCH 16/36] Added note on Exception schema. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bbd2a1b3..1d375c54 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,9 @@ build the following from your existing routes and models without additional effo - Sub resources - Schema -SwaggerBake works with your existing YML definitions and will not overwrite anything. +SwaggerBake works with your existing YML definitions and will not overwrite anything. By default, it uses +components > schemas > Exception as your Swagger documentations Exception schema. See the default +[swagger.yml](assets/swagger.yml). ## Doc Blocks From 59e529a8ff527210e53ae3a31919adb5a12bf8a8 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 20:19:32 -0400 Subject: [PATCH 17/36] configuration updates --- README.md | 2 +- assets/swagger_bake.php | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1d375c54..6c220141 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ build the following from your existing routes and models without additional effo SwaggerBake works with your existing YML definitions and will not overwrite anything. By default, it uses components > schemas > Exception as your Swagger documentations Exception schema. See the default -[swagger.yml](assets/swagger.yml). +[swagger.yml](assets/swagger.yml) and `exceptionSchema` in [swagger_bake.php](assets/swagger_bake.php) for more info. ## Doc Blocks diff --git a/assets/swagger_bake.php b/assets/swagger_bake.php index a9dd0ea9..e9fb2412 100644 --- a/assets/swagger_bake.php +++ b/assets/swagger_bake.php @@ -13,6 +13,8 @@ * * @var string $docType: Options are swagger and redoc, defaults: swagger * + * @var string $exceptionSchema: The name of your Exception schema in your swagger.yml definition file. + * * @var array $namespaces: Can be used if your controllers or entities exist in non-standard namespace such as a plugin */ return [ @@ -21,7 +23,9 @@ 'yml' => '/config/swagger.yml', 'json' => '/webroot/swagger.json', 'webPath' => '/swagger.json', - 'hotReload' => false, + 'hotReload' => \Cake\Core\Configure::read('debug'), + /** optional configurations below: **/ + /* 'docType' => 'swagger', 'exceptionSchema' => 'Exception', 'namespaces' => [ @@ -29,6 +33,7 @@ 'entities' => ['\App\\'], 'tables' => ['\App\\'] ] + */ ] ]; From fd653d6952800dab303e764788b29fe891db02bd Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 20:23:13 -0400 Subject: [PATCH 18/36] added comments --- src/Lib/Swagger.php | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index 3565fa0a..9b392a40 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -162,6 +162,9 @@ public function getConfig() : Configuration return $this->config; } + /** + * Builds schemas from cake models + */ private function buildSchemas(): void { $schemaFactory = new Factory\SchemaFactory($this->config); @@ -179,6 +182,9 @@ private function buildSchemas(): void } } + /** + * Builds paths from cake routes + */ private function buildPaths(): void { $routes = $this->cakeRoute->getRoutes(); @@ -202,12 +208,26 @@ private function buildPaths(): void } } + /** + * Sets security on a path + * + * @param Path $path + * @param ExpressiveRoute $route + * @return Path + */ private function pathWithSecurity(Path $path, ExpressiveRoute $route) : Path { $path->setSecurity((new Security($route, $this->config))->getPathSecurity()); return $path; } + /** + * Sets header parameters on a path + * + * @param Path $path + * @param ExpressiveRoute $route + * @return Path + */ private function pathWithParameters(Path $path, ExpressiveRoute $route) : Path { $headers = (new HeaderParameter($route, $this->config))->getHeaderParameters(); @@ -222,6 +242,12 @@ private function pathWithParameters(Path $path, ExpressiveRoute $route) : Path return $path; } + /** + * Sets responses on a path + * + * @param Path $path + * @return Path + */ private function pathWithResponses(Path $path) : Path { foreach ($path->getTags() as $tag) { @@ -257,6 +283,13 @@ private function pathWithResponses(Path $path) : Path return $path; } + /** + * Sets a request body on a path + * + * @param Path $path + * @param ExpressiveRoute $route + * @return Path + */ private function pathWithRequestBody(Path $path, ExpressiveRoute $route) : Path { $requestBody = (new RequestBodyBuilder($path, $this, $route))->build(); @@ -266,7 +299,10 @@ private function pathWithRequestBody(Path $path, ExpressiveRoute $route) : Path return $path; } - private function buildFromDefaults() + /** + * Constructs the primary array used in this class from pre-defined swagger.yml + */ + private function buildFromDefaults() : void { $array = Yaml::parseFile($this->config->getYml()); if (!isset($array['paths'])) { From 781589adc334578f20bb532a950f976b94b81a3d Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 20:26:16 -0400 Subject: [PATCH 19/36] Code reduction and indent reduction --- src/Lib/Swagger.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index 9b392a40..f1b09e84 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -254,12 +254,13 @@ private function pathWithResponses(Path $path) : Path $className = Inflector::classify($tag); if (!$path->hasSuccessResponseCode() && $this->getSchemaByName($className)) { - $response = new Response(); - $response - ->setSchemaRef('#/components/schemas/' . $className) - ->setCode(200); - $path->pushResponse($response); + continue; } + + $response = (new Response()) + ->setSchemaRef('#/components/schemas/' . $className) + ->setCode(200); + $path->pushResponse($response); } if (!$path->hasSuccessResponseCode()) { From 9cc596e5adbd26753a92bc237707e539dba1450f Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 20:29:58 -0400 Subject: [PATCH 20/36] Was not setting default schema correctly after prev commit. - Do not set default schema if - success response is set - or - no schema exists --- src/Lib/Swagger.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index f1b09e84..79a6c01a 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -253,7 +253,7 @@ private function pathWithResponses(Path $path) : Path foreach ($path->getTags() as $tag) { $className = Inflector::classify($tag); - if (!$path->hasSuccessResponseCode() && $this->getSchemaByName($className)) { + if ($path->hasSuccessResponseCode() || !$this->getSchemaByName($className)) { continue; } From 4a0b5767d11d52280f412bdabfe8fe63742cd264 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 20:50:43 -0400 Subject: [PATCH 21/36] Added support for Array of objects on collections If the path operation is a GET and an index action, an array of an object is set as the response type. --- src/Lib/OpenApi/Schema.php | 35 +++++++++++++++++++++++++++++++++++ src/Lib/Swagger.php | 26 ++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Lib/OpenApi/Schema.php b/src/Lib/OpenApi/Schema.php index 77d2fa9d..a71c6f81 100644 --- a/src/Lib/OpenApi/Schema.php +++ b/src/Lib/OpenApi/Schema.php @@ -7,12 +7,24 @@ class Schema implements JsonSerializable { + /** @var string */ private $name = ''; + + /** @var string */ private $description = ''; + + /** @var string */ private $type = ''; + + /** @var array */ private $required = []; + + /** @var array */ private $properties = []; + /** @var array */ + private $items = []; + public function toArray() : array { $vars = get_object_vars($this); @@ -25,6 +37,9 @@ public function toArray() : array if (empty($vars['properties'])) { unset($vars['properties']); } + if (empty($vars['items'])) { + unset($vars['items']); + } return $vars; } @@ -149,4 +164,24 @@ public function setDescription(string $description): Schema $this->description = $description; return $this; } + + /** + * @return array + */ + public function getItems(): array + { + return $this->items; + } + + /** + * @param array $items + * @return Schema + */ + public function setItems(array $items): Schema + { + $this->items = $items; + return $this; + } + + } \ No newline at end of file diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index 79a6c01a..20dd4db8 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -51,8 +51,13 @@ public function getArray(): array } } - ksort($this->array['paths']); - ksort($this->array['components']['schemas']); + ksort($this->array['paths'], SORT_STRING); + uksort($this->array['components']['schemas'], function ($a, $b) { + return strcasecmp( + preg_replace('/\s+/', '', $a), + preg_replace('/\s+/', '', $b) + ); + }); if (empty($this->array['components']['schemas'])) { unset($this->array['components']['schemas']); @@ -257,6 +262,23 @@ private function pathWithResponses(Path $path) : Path continue; } + if ($path->getType() == 'get' && strstr($path->getOperationId(),':index')) { + $tags = $path->getTags(); + $tag = preg_replace('/\s+/', '', reset($tags)); + $schema = (new Schema()) + ->setName($tag) + ->setType('array') + ->setItems(['$ref' => '#/components/schemas/' . $className]) + ; + $this->pushSchema($schema); + + $response = (new Response()) + ->setSchemaRef('#/components/schemas/' . $tag) + ->setCode(200); + $path->pushResponse($response); + continue; + } + $response = (new Response()) ->setSchemaRef('#/components/schemas/' . $className) ->setCode(200); From 704bb199efcd09b2a759e0083dd99f6b698fec60 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 21:06:59 -0400 Subject: [PATCH 22/36] new exceptionSchema config broke unit tests --- tests/TestCase/Lib/SwaggerEntityTest.php | 50 ++++++--------------- tests/TestCase/Lib/SwaggerOperationTest.php | 1 + tests/TestCase/Lib/SwaggerPathTest.php | 1 + tests/TestCase/Lib/SwaggerSchemaTest.php | 1 + tests/TestCase/Lib/SwaggerTest.php | 32 ++++++------- 5 files changed, 29 insertions(+), 56 deletions(-) diff --git a/tests/TestCase/Lib/SwaggerEntityTest.php b/tests/TestCase/Lib/SwaggerEntityTest.php index 6b6ea3d8..ecd76c49 100644 --- a/tests/TestCase/Lib/SwaggerEntityTest.php +++ b/tests/TestCase/Lib/SwaggerEntityTest.php @@ -35,17 +35,13 @@ public function setUp(): void }); $this->router = $router; - AnnotationLoader::load(); - } - - public function testEntityExists() - { - $config = new Configuration([ + $this->config = new Configuration([ 'prefix' => '/api', 'yml' => '/config/swagger-bare-bones.yml', 'json' => '/webroot/swagger.json', 'webPath' => '/swagger.json', 'hotReload' => false, + 'exceptionSchema' => 'Exception', 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], @@ -53,9 +49,14 @@ public function testEntityExists() ] ], SWAGGER_BAKE_TEST_APP); - $cakeRoute = new CakeRoute($this->router, $config); + AnnotationLoader::load(); + } + + public function testEntityExists() + { + $cakeRoute = new CakeRoute($this->router, $this->config); - $swagger = new Swagger(new CakeModel($cakeRoute, $config)); + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); $arr = json_decode($swagger->toString(), true); @@ -64,22 +65,9 @@ public function testEntityExists() public function testEntityInvisible() { - $config = new Configuration([ - 'prefix' => '/api', - 'yml' => '/config/swagger-bare-bones.yml', - 'json' => '/webroot/swagger.json', - 'webPath' => '/swagger.json', - 'hotReload' => false, - 'namespaces' => [ - 'controllers' => ['\SwaggerBakeTest\App\\'], - 'entities' => ['\SwaggerBakeTest\App\\'], - 'tables' => ['\SwaggerBakeTest\App\\'], - ] - ], SWAGGER_BAKE_TEST_APP); - - $cakeRoute = new CakeRoute($this->router, $config); + $cakeRoute = new CakeRoute($this->router, $this->config); - $swagger = new Swagger(new CakeModel($cakeRoute, $config)); + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); $arr = json_decode($swagger->toString(), true); @@ -88,21 +76,9 @@ public function testEntityInvisible() public function testEntityAttribute() { - $config = new Configuration([ - 'prefix' => '/api', - 'yml' => '/config/swagger-bare-bones.yml', - 'json' => '/webroot/swagger.json', - 'webPath' => '/swagger.json', - 'hotReload' => false, - 'namespaces' => [ - 'controllers' => ['\SwaggerBakeTest\App\\'], - 'entities' => ['\SwaggerBakeTest\App\\'] - ] - ], SWAGGER_BAKE_TEST_APP); - - $cakeRoute = new CakeRoute($this->router, $config); + $cakeRoute = new CakeRoute($this->router, $this->config); - $swagger = new Swagger(new CakeModel($cakeRoute, $config)); + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); $arr = json_decode($swagger->toString(), true); diff --git a/tests/TestCase/Lib/SwaggerOperationTest.php b/tests/TestCase/Lib/SwaggerOperationTest.php index 3c7e6eb0..6dc8a426 100644 --- a/tests/TestCase/Lib/SwaggerOperationTest.php +++ b/tests/TestCase/Lib/SwaggerOperationTest.php @@ -59,6 +59,7 @@ public function setUp(): void 'json' => '/webroot/swagger.json', 'webPath' => '/swagger.json', 'hotReload' => false, + 'exceptionSchema' => 'Exception', 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], diff --git a/tests/TestCase/Lib/SwaggerPathTest.php b/tests/TestCase/Lib/SwaggerPathTest.php index 596eea35..5e60cc0f 100644 --- a/tests/TestCase/Lib/SwaggerPathTest.php +++ b/tests/TestCase/Lib/SwaggerPathTest.php @@ -46,6 +46,7 @@ public function testPathInvisible() 'json' => '/webroot/swagger.json', 'webPath' => '/swagger.json', 'hotReload' => false, + 'exceptionSchema' => 'Exception', 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], diff --git a/tests/TestCase/Lib/SwaggerSchemaTest.php b/tests/TestCase/Lib/SwaggerSchemaTest.php index 033a976c..cbc656a5 100644 --- a/tests/TestCase/Lib/SwaggerSchemaTest.php +++ b/tests/TestCase/Lib/SwaggerSchemaTest.php @@ -35,6 +35,7 @@ public function setUp(): void 'json' => '/webroot/swagger.json', 'webPath' => '/swagger.json', 'hotReload' => false, + 'exceptionSchema' => 'Exception', 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], diff --git a/tests/TestCase/Lib/SwaggerTest.php b/tests/TestCase/Lib/SwaggerTest.php index b41b1d2c..a1a29c8b 100644 --- a/tests/TestCase/Lib/SwaggerTest.php +++ b/tests/TestCase/Lib/SwaggerTest.php @@ -50,23 +50,26 @@ public function setUp(): void }); $this->router = $router; - AnnotationLoader::load(); - } - - public function testGetArrayWithExistingPathsAndSchema() - { - $config = new Configuration([ + $this->config = [ 'prefix' => '/api', 'yml' => '/config/swagger-with-existing.yml', 'json' => '/webroot/swagger.json', 'webPath' => '/swagger.json', 'hotReload' => false, + 'exceptionSchema' => 'Exception', 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], 'tables' => ['\SwaggerBakeTest\App\\'], ] - ], SWAGGER_BAKE_TEST_APP); + ]; + + AnnotationLoader::load(); + } + + public function testGetArrayWithExistingPathsAndSchema() + { + $config = new Configuration($this->config, SWAGGER_BAKE_TEST_APP); $cakeRoute = new CakeRoute($this->router, $config); @@ -80,18 +83,9 @@ public function testGetArrayWithExistingPathsAndSchema() public function testGetArrayFromBareBones() { - $config = new Configuration([ - 'prefix' => '/api', - 'yml' => '/config/swagger-bare-bones.yml', - 'json' => '/webroot/swagger.json', - 'webPath' => '/swagger.json', - 'hotReload' => false, - 'namespaces' => [ - 'controllers' => ['\SwaggerBakeTest\App\\'], - 'entities' => ['\SwaggerBakeTest\App\\'], - 'tables' => ['\SwaggerBakeTest\App\\'], - ] - ], SWAGGER_BAKE_TEST_APP); + $vars = $this->config; + $vars['yml'] = '/config/swagger-bare-bones.yml'; + $config = new Configuration($vars, SWAGGER_BAKE_TEST_APP); $cakeRoute = new CakeRoute($this->router, $config); From a556b7254d75d45883dc7798332978254f31e015 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 21:08:11 -0400 Subject: [PATCH 23/36] fixed undefined/warning issue for properties --- src/Lib/Swagger.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index 20dd4db8..c80c0a9d 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -336,10 +336,12 @@ private function buildFromDefaults() : void } foreach ($array['components']['schemas'] as $schemaName => $schemaVar) { + $schema = (new Schema()) ->setName($schemaName) - ->setType($schemaVar['type']); + ->setType(isset($schemaVar['type'])); + $schemaVar['properties'] = $schemaVar['properties'] ?? []; foreach ($schemaVar['properties'] as $propertyName => $propertyVar) { $property = (new SchemaProperty()) ->setType($propertyVar['type']) From afb8a5ada1e3ee7b717aae10863557b442dfebab Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 22:27:37 -0400 Subject: [PATCH 24/36] code reduction --- src/Lib/RequestBodyBuilder.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Lib/RequestBodyBuilder.php b/src/Lib/RequestBodyBuilder.php index 0e33fbe3..08c89121 100644 --- a/src/Lib/RequestBodyBuilder.php +++ b/src/Lib/RequestBodyBuilder.php @@ -46,13 +46,11 @@ public function build() : ?RequestBody return null; } - $content = new Content(); - $content + $content = (new Content()) ->setMimeType('application/x-www-form-urlencoded') ->setSchema($schema); ; - $requestBody ->pushContent($content) ->setRequired(true) From ad21b530d1157c084e8f2dbdb4aca207fac24baa Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 23:32:17 -0400 Subject: [PATCH 25/36] #22 Fixed @SwagRequestBodyContent - SwagRequestBodyContent mimeType was being cast to a bool instead of a string - Content->setSchema was changed to accept both string and Schema. If string, set a $ref --- src/Lib/Annotation/SwagRequestBodyContent.php | 5 +---- src/Lib/OpenApi/Content.php | 10 ++++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Lib/Annotation/SwagRequestBodyContent.php b/src/Lib/Annotation/SwagRequestBodyContent.php index bf115343..9a2b30ba 100644 --- a/src/Lib/Annotation/SwagRequestBodyContent.php +++ b/src/Lib/Annotation/SwagRequestBodyContent.php @@ -20,13 +20,10 @@ class SwagRequestBodyContent /** @var string */ public $mimeType; - /** @var bool */ - public $ignoreCakeSchema; - public function __construct(array $values) { $values = array_merge(['refEntity' => '', 'mimeType' => 'text/plain'], $values); $this->refEntity = $values['refEntity']; - $this->mimeType = (bool) $values['mimeType']; + $this->mimeType = $values['mimeType']; } } \ No newline at end of file diff --git a/src/Lib/OpenApi/Content.php b/src/Lib/OpenApi/Content.php index fe439407..671e4aee 100644 --- a/src/Lib/OpenApi/Content.php +++ b/src/Lib/OpenApi/Content.php @@ -14,6 +14,10 @@ public function toArray() : array { $vars = get_object_vars($this); unset($vars['mimeType']); + if (is_string($this->schema)) { + unset($vars['schema']); + $vars['schema']['$ref'] = $this->schema; + } return $vars; } @@ -49,10 +53,12 @@ public function getSchema() : Schema } /** - * @param mixed $schema + * Can be either a schema $ref string such as '#/components/schemas/Pet' or a Schema instance. + * + * @param string|Schema $schema * @return Content */ - public function setSchema(Schema $schema) : Content + public function setSchema($schema) : Content { $this->schema = $schema; return $this; From 4b8589be0ca28e8f7157220cf30b8b1f59be113a Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 23:41:14 -0400 Subject: [PATCH 26/36] #23 Convert data types to swagger data types. --- src/Lib/Utility/DataTypeConversion.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Lib/Utility/DataTypeConversion.php b/src/Lib/Utility/DataTypeConversion.php index 9b5e8362..e5df5398 100644 --- a/src/Lib/Utility/DataTypeConversion.php +++ b/src/Lib/Utility/DataTypeConversion.php @@ -15,6 +15,11 @@ public static function convert(string $type) : string case 'biginteger': case 'mediuminteger': return 'integer'; + case 'decimal': + case 'float': + return 'number'; + case 'uuid': + case 'text': case 'varchar': case 'char': case 'date': From 58a79a67482293dabf24dcee078e2c23f3676b61 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Wed, 29 Apr 2020 23:47:01 -0400 Subject: [PATCH 27/36] #24 --- src/Lib/Swagger.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index c80c0a9d..dc83085d 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -339,9 +339,11 @@ private function buildFromDefaults() : void $schema = (new Schema()) ->setName($schemaName) - ->setType(isset($schemaVar['type'])); + ->setType($schemaVar['type']) + ->setDescription($schemaVar['description'] ?? ''); $schemaVar['properties'] = $schemaVar['properties'] ?? []; + foreach ($schemaVar['properties'] as $propertyName => $propertyVar) { $property = (new SchemaProperty()) ->setType($propertyVar['type']) From 9f16808e30e7c1b53be4d1529ff9deb6c21bc1db Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 16:31:48 -0400 Subject: [PATCH 28/36] #22 Allow user to define multiple request body types This commit prevents an early exit if @SwagRequestBody is not defined. The result was additional request bodies were not being added into Swagger. --- src/Lib/Factory/PathFactory.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Lib/Factory/PathFactory.php b/src/Lib/Factory/PathFactory.php index 8360e26f..dd33706b 100644 --- a/src/Lib/Factory/PathFactory.php +++ b/src/Lib/Factory/PathFactory.php @@ -15,6 +15,7 @@ use SwaggerBake\Lib\OpenApi\OperationExternalDoc; use SwaggerBake\Lib\OpenApi\Path; use SwaggerBake\Lib\OpenApi\Parameter; +use SwaggerBake\Lib\OpenApi\RequestBody; use SwaggerBake\Lib\OpenApi\Response; use SwaggerBake\Lib\OpenApi\Schema; use SwaggerBake\Lib\Utility\AnnotationUtility; @@ -187,16 +188,14 @@ private function withRequestBody(Path $path, array $annotations) : Path return $path; } + $requestBody = new RequestBody(); + foreach ($annotations as $annotation) { if ($annotation instanceof SwagAnnotation\SwagRequestBody) { $requestBody = (new SwagAnnotation\SwagRequestBodyHandler())->getResponse($annotation); } } - if (!isset($requestBody)) { - return $path; - } - foreach ($annotations as $annotation) { if ($annotation instanceof SwagAnnotation\SwagRequestBodyContent) { $requestBody->pushContent( From 003d3b4c86af722d2c27feb6214f099a7db67ec6 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 19:07:08 -0400 Subject: [PATCH 29/36] added comments --- src/Lib/OpenApi/Parameter.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Lib/OpenApi/Parameter.php b/src/Lib/OpenApi/Parameter.php index 7ba49321..46378687 100644 --- a/src/Lib/OpenApi/Parameter.php +++ b/src/Lib/OpenApi/Parameter.php @@ -12,12 +12,25 @@ */ class Parameter implements JsonSerializable { + /** @var string **/ private $name = ''; + + /** @var string **/ private $in = ''; + + /** @var string **/ private $description = ''; + + /** @var bool **/ private $required = false; + + /** @var Schema **/ private $schema; + + /** @var bool **/ private $deprecated = false; + + /** @var bool **/ private $allowEmptyValue = true; public function toArray() : array From 7eba89ef1deb279212aef725966c0a3a0346b77c Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 22:56:56 -0400 Subject: [PATCH 30/36] #20 Support defining descriptions on Query and Post parameters --- src/Lib/Annotation/SwagForm.php | 9 +++++++-- src/Lib/Annotation/SwagFormHandler.php | 1 + src/Lib/Annotation/SwagQuery.php | 7 ++++++- src/Lib/Annotation/SwagQueryHandler.php | 1 + src/Lib/OpenApi/SchemaProperty.php | 21 +++++++++++++++++++++ 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/Lib/Annotation/SwagForm.php b/src/Lib/Annotation/SwagForm.php index fa404c23..39c6a32f 100644 --- a/src/Lib/Annotation/SwagForm.php +++ b/src/Lib/Annotation/SwagForm.php @@ -12,7 +12,8 @@ * @Attributes({ * @Attribute("name", type = "string"), * @Attribute("type", type = "string"), - * @Attribute("required", type = "bool"), + * @Attribute("description", type="string"), + * @Attribute("required", type="boolean"), * }) */ class SwagForm @@ -23,6 +24,9 @@ class SwagForm /** @var string */ public $type; + /** @var string */ + public $description; + /** @var bool */ public $required; @@ -32,7 +36,7 @@ public function __construct(array $values) throw new InvalidArgumentException('Name parameter is required'); } - $values = array_merge(['type' => 'string', 'required' => false], $values); + $values = array_merge(['type' => 'string', 'description' => '', 'required' => false], $values); if (!in_array($values['type'], OpenApiDataType::TYPES)) { $type = $values['type']; @@ -47,6 +51,7 @@ public function __construct(array $values) $this->name = $values['name']; $this->type = $values['type']; + $this->description = $values['example']; $this->required = (bool) $values['required']; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagFormHandler.php b/src/Lib/Annotation/SwagFormHandler.php index 4348ecea..c9a11328 100644 --- a/src/Lib/Annotation/SwagFormHandler.php +++ b/src/Lib/Annotation/SwagFormHandler.php @@ -10,6 +10,7 @@ public function getSchemaProperty(SwagForm $annotation) : SchemaProperty { $schemaProperty = new SchemaProperty(); $schemaProperty + ->setDescription($annotation->description) ->setName($annotation->name) ->setType($annotation->type) ->setRequired($annotation->required) diff --git a/src/Lib/Annotation/SwagQuery.php b/src/Lib/Annotation/SwagQuery.php index 34a0b305..a9357518 100644 --- a/src/Lib/Annotation/SwagQuery.php +++ b/src/Lib/Annotation/SwagQuery.php @@ -12,6 +12,7 @@ * @Attributes({ * @Attribute("name", type="string"), * @Attribute("type", type="string"), + * @Attribute("description", type="string"), * @Attribute("required", type="boolean"), * }) */ @@ -23,6 +24,9 @@ class SwagQuery /** @var string */ public $type; + /** @var string */ + public $description; + /** @var bool */ public $required; @@ -32,7 +36,7 @@ public function __construct(array $values) throw new InvalidArgumentException('Name parameter is required'); } - $values = array_merge(['type' => 'string', 'required' => false], $values); + $values = array_merge(['type' => 'string', 'description' => '', 'required' => false], $values); if (!in_array($values['type'], OpenApiDataType::TYPES)) { $type = $values['type']; @@ -45,6 +49,7 @@ public function __construct(array $values) $this->name = $values['name']; $this->type = $values['type']; + $this->description = $values['description']; $this->required = (bool) $values['required']; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagQueryHandler.php b/src/Lib/Annotation/SwagQueryHandler.php index f80c3ec2..b5eaf05f 100644 --- a/src/Lib/Annotation/SwagQueryHandler.php +++ b/src/Lib/Annotation/SwagQueryHandler.php @@ -12,6 +12,7 @@ public function getQueryParameter(SwagQuery $annotation) : Parameter $parameter = new Parameter(); $parameter ->setName($annotation->name) + ->setDescription($annotation->description) ->setAllowEmptyValue(false) ->setDeprecated(false) ->setRequired($annotation->required) diff --git a/src/Lib/OpenApi/SchemaProperty.php b/src/Lib/OpenApi/SchemaProperty.php index bba18f19..19080f2d 100644 --- a/src/Lib/OpenApi/SchemaProperty.php +++ b/src/Lib/OpenApi/SchemaProperty.php @@ -19,6 +19,9 @@ class SchemaProperty implements JsonSerializable /** @var string */ private $example = ''; + /** @var string */ + private $description = ''; + /** @var bool */ private $readOnly = false; @@ -166,4 +169,22 @@ public function setExample(string $example): SchemaProperty $this->example = $example; return $this; } + + /** + * @return string + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * @param string $description + * @return SchemaProperty + */ + public function setDescription(string $description): SchemaProperty + { + $this->description = $description; + return $this; + } } \ No newline at end of file From 72b17b4a4024e517e9b17c6c00824b45dc2fd04a Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 23:02:35 -0400 Subject: [PATCH 31/36] #20 invalid name, ref description not example --- src/Lib/Annotation/SwagForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lib/Annotation/SwagForm.php b/src/Lib/Annotation/SwagForm.php index 39c6a32f..b5c14a9a 100644 --- a/src/Lib/Annotation/SwagForm.php +++ b/src/Lib/Annotation/SwagForm.php @@ -51,7 +51,7 @@ public function __construct(array $values) $this->name = $values['name']; $this->type = $values['type']; - $this->description = $values['example']; + $this->description = $values['description']; $this->required = (bool) $values['required']; } } \ No newline at end of file From cf88d2880e8ace6053817a9df9d3b1ee41235656 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 23:11:50 -0400 Subject: [PATCH 32/36] Support defining descriptions for cake model attributes #19 --- src/Lib/Annotation/SwagEntityAttribute.php | 5 +++++ src/Lib/Annotation/SwagEntityAttributeHandler.php | 1 + src/Lib/OpenApi/SchemaProperty.php | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/src/Lib/Annotation/SwagEntityAttribute.php b/src/Lib/Annotation/SwagEntityAttribute.php index 695ee71c..8b7db235 100644 --- a/src/Lib/Annotation/SwagEntityAttribute.php +++ b/src/Lib/Annotation/SwagEntityAttribute.php @@ -10,6 +10,7 @@ * @Attributes({ * @Attribute("name", type = "string"), * @Attribute("type", type = "string"), + * @Attribute("description", type = "string"), * @Attribute("readOnly", type = "bool"), * @Attribute("writeOnly", type = "bool"), * @Attribute("required", type = "bool"), @@ -23,6 +24,9 @@ class SwagEntityAttribute /** @var string */ public $type; + /** @var string */ + public $description; + /** @var bool */ public $readOnly; @@ -45,6 +49,7 @@ public function __construct(array $values) $this->name = $values['name']; $this->type = $values['type']; + $this->description = $values['description']; $this->readOnly = $values['readOnly']; $this->writeOnly = $values['writeOnly']; $this->required = $values['required']; diff --git a/src/Lib/Annotation/SwagEntityAttributeHandler.php b/src/Lib/Annotation/SwagEntityAttributeHandler.php index d4a9ee96..d9e8e35b 100644 --- a/src/Lib/Annotation/SwagEntityAttributeHandler.php +++ b/src/Lib/Annotation/SwagEntityAttributeHandler.php @@ -11,6 +11,7 @@ public function getSchemaProperty(SwagEntityAttribute $annotation) : SchemaPrope $schemaProperty = new SchemaProperty(); $schemaProperty ->setName($annotation->name) + ->setDescription($annotation->description) ->setType($annotation->type) ->setReadOnly($annotation->readOnly) ->setWriteOnly($annotation->writeOnly) diff --git a/src/Lib/OpenApi/SchemaProperty.php b/src/Lib/OpenApi/SchemaProperty.php index 19080f2d..ef0cc1d9 100644 --- a/src/Lib/OpenApi/SchemaProperty.php +++ b/src/Lib/OpenApi/SchemaProperty.php @@ -36,6 +36,14 @@ public function toArray() : array $vars = get_object_vars($this); unset($vars['name']); unset($vars['required']); + + if (empty($vars['example'])) { + unset($vars['example']); + } + if (empty($vars['description'])) { + unset($vars['description']); + } + return $vars; } From 09b979f70788006a8dc9497ca72a82827e138f97 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 23:16:47 -0400 Subject: [PATCH 33/36] Support defining descriptions for @SwagHeader --- src/Lib/Annotation/SwagHeader.php | 7 ++++++- src/Lib/Annotation/SwagHeaderHandler.php | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Lib/Annotation/SwagHeader.php b/src/Lib/Annotation/SwagHeader.php index 20de69f4..c7f725fd 100644 --- a/src/Lib/Annotation/SwagHeader.php +++ b/src/Lib/Annotation/SwagHeader.php @@ -10,6 +10,7 @@ * @Attributes({ * @Attribute("name", type = "string"), * @Attribute("type", type = "string"), + * @Attribute("description", type = "string"), * @Attribute("required", type = "bool"), * }) */ @@ -21,6 +22,9 @@ class SwagHeader /** @var string */ public $type; + /** @var string */ + public $description; + /** @var bool */ public $required; @@ -30,10 +34,11 @@ public function __construct(array $values) throw new InvalidArgumentException('Name parameter is required'); } - $values = array_merge(['type' => 'string', 'required' => false], $values); + $values = array_merge(['type' => 'string', 'description' => '', 'required' => false], $values); $this->name = $values['name']; $this->type = $values['type']; + $this->description = $values['description']; $this->required = (bool) $values['required']; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagHeaderHandler.php b/src/Lib/Annotation/SwagHeaderHandler.php index f09b2b3b..7a31b0a9 100644 --- a/src/Lib/Annotation/SwagHeaderHandler.php +++ b/src/Lib/Annotation/SwagHeaderHandler.php @@ -12,6 +12,7 @@ public function getHeaderParameters(SwagHeader $annotation) : Parameter $parameter = new Parameter(); $parameter ->setName($annotation->name) + ->setDescription($annotation->description) ->setAllowEmptyValue(false) ->setDeprecated(false) ->setRequired($annotation->required) From e3db51a9c012eba3eb553083670bc59ed6ad22a5 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 23:17:36 -0400 Subject: [PATCH 34/36] updated readme --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6c220141..dc04ca36 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Method level annotation for adding query parameters. ```php /** - * @Swag\SwagQuery(name="queryParamName", type="string", required=false) + * @Swag\SwagQuery(name="queryParamName", type="string", description="string", required=false) */ public function index() {} ``` @@ -135,7 +135,7 @@ Method level annotation for adding form data fields. ```php /** - * @Swag\SwagForm(name="fieldName", type="string", required=false) + * @Swag\SwagForm(name="fieldName", type="string", description="string", required=false) */ public function index() {} ``` @@ -145,7 +145,7 @@ Method level annotation for adding header parameters. ```php /** - * @Swag\SwagHeader(name="X-HEAD-ATTRIBUTE", type="string", required=false) + * @Swag\SwagHeader(name="X-HEAD-ATTRIBUTE", type="string", description="string", required=false) */ public function index() {} ``` @@ -185,8 +185,7 @@ Method level annotation for describing custom content in request body. ```php /** - * @Swag\SwagRequestBodyContent(refEntity="#/components/schemas/Lead", mimeType="application/x-www-form-urlencoded") - * @Swag\SwagRequestBodyContent(refEntity="", mimeType="text/plain") + * @Swag\SwagRequestBodyContent(refEntity="#/components/schemas/Lead", mimeType="application/json") */ public function index() {} ``` @@ -227,7 +226,7 @@ Class level annotation for customizing Schema Attributes with @SwagEntityAttribu ```php /** - * @Swag\SwagEntityAttribute(name="modified", type="string", readOnly=true, required=false) + * @Swag\SwagEntityAttribute(name="modified", type="string", description="string", readOnly=true, required=false) */ class Employee extends Entity { ``` From 5e36ba9bfdfb2b131262a439a77df48fe5b2080d Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 23:19:05 -0400 Subject: [PATCH 35/36] conditionally return description --- src/Lib/OpenApi/Parameter.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Lib/OpenApi/Parameter.php b/src/Lib/OpenApi/Parameter.php index 46378687..e705f0e9 100644 --- a/src/Lib/OpenApi/Parameter.php +++ b/src/Lib/OpenApi/Parameter.php @@ -40,7 +40,11 @@ public function toArray() : array public function jsonSerialize() { - return $this->toArray(); + $vars = $this->toArray(); + if (empty($vars['description'])) { + unset($vars['description']); + } + return $vars; } /** From ac89cd0a3be2c80e1a0c61893dac55cd23eaff76 Mon Sep 17 00:00:00 2001 From: chris cnizzardini Date: Thu, 30 Apr 2020 23:20:51 -0400 Subject: [PATCH 36/36] fixed undefined notice --- src/Lib/Annotation/SwagEntityAttribute.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lib/Annotation/SwagEntityAttribute.php b/src/Lib/Annotation/SwagEntityAttribute.php index 8b7db235..7a020970 100644 --- a/src/Lib/Annotation/SwagEntityAttribute.php +++ b/src/Lib/Annotation/SwagEntityAttribute.php @@ -43,7 +43,7 @@ public function __construct(array $values) } $values = array_merge( - ['type' => 'string', 'readOnly' => false, 'writeOnly' => false, 'required' => false], + ['type' => 'string', 'description' => '', 'readOnly' => false, 'writeOnly' => false, 'required' => false], $values );