From 388368e78cfa73105ef4bcd3d34370c4ac2d75a8 Mon Sep 17 00:00:00 2001 From: Yuriy Belenko Date: Tue, 5 Apr 2022 05:47:12 +0300 Subject: [PATCH 1/2] Setup Data Mocker as dev dependency New 1.2.0 version of the package contains required factory class. --- .../php-slim4-server/README.mustache | 16 +++++++++ .../php-slim4-server/composer.mustache | 2 +- .../config_dev_default.mustache | 33 +++++++++++++++++++ .../register_dependencies.mustache | 9 +++++ .../php-slim4-server/register_routes.mustache | 17 ++++++++++ 5 files changed, 76 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/php-slim4-server/README.mustache b/modules/openapi-generator/src/main/resources/php-slim4-server/README.mustache index dcd719301231..f49649f1cfe0 100644 --- a/modules/openapi-generator/src/main/resources/php-slim4-server/README.mustache +++ b/modules/openapi-generator/src/main/resources/php-slim4-server/README.mustache @@ -115,6 +115,22 @@ Switch your app environment to development in `public/.htaccess` file: ``` +## Mock Server +Since this feature should be used for development only, change environment to `development` and send additional HTTP header `X-{{invokerPackage}}-Mock: ping` with any request to get mocked response. +CURL example: +```console +curl --request GET \ + --url 'http://localhost:8888/v2/pet/findByStatus?status=available' \ + --header 'accept: application/json' \ + --header 'X-{{invokerPackage}}-Mock: ping' +[{"id":-8738629417578509312,"category":{"id":-4162503862215270400,"name":"Lorem ipsum dol"},"name":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem i","photoUrls":["Lor"],"tags":[{"id":-3506202845849391104,"name":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectet"}],"status":"pending"}] +``` + +Used packages: +* [Openapi Data Mocker](https://github.com/ybelenko/openapi-data-mocker) - first implementation of OAS3 fake data generator. +* [Openapi Data Mocker Server Middleware](https://github.com/ybelenko/openapi-data-mocker-server-middleware) - PSR-15 HTTP server middleware. +* [Openapi Data Mocker Interfaces](https://github.com/ybelenko/openapi-data-mocker-interfaces) - package with mocking interfaces. + {{#generateApiDocs}} ## API Endpoints diff --git a/modules/openapi-generator/src/main/resources/php-slim4-server/composer.mustache b/modules/openapi-generator/src/main/resources/php-slim4-server/composer.mustache index f05b9c846c49..310edcb04f96 100644 --- a/modules/openapi-generator/src/main/resources/php-slim4-server/composer.mustache +++ b/modules/openapi-generator/src/main/resources/php-slim4-server/composer.mustache @@ -27,7 +27,7 @@ "slim/psr7": "^1.1.0", {{/isSlimPsr7}} "ybelenko/openapi-data-mocker": "^1.0", - "ybelenko/openapi-data-mocker-server-middleware": "^1.0" + "ybelenko/openapi-data-mocker-server-middleware": "^1.2" }, "require-dev": { "overtrue/phplint": "^2.0.2", diff --git a/modules/openapi-generator/src/main/resources/php-slim4-server/config_dev_default.mustache b/modules/openapi-generator/src/main/resources/php-slim4-server/config_dev_default.mustache index 5d5845b3cc17..5bdfb9f18774 100644 --- a/modules/openapi-generator/src/main/resources/php-slim4-server/config_dev_default.mustache +++ b/modules/openapi-generator/src/main/resources/php-slim4-server/config_dev_default.mustache @@ -62,4 +62,37 @@ return [ 'pdo.options' => [ \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, ], + + // mocker + // OBVIOUSLY MUST NOT BE USED for production + // @see https://github.com/ybelenko/openapi-data-mocker-server-middleware + 'mocker.getMockStatusCodeCallback' => function () { + return function (\Psr\Http\Message\ServerRequestInterface $request, array $responses) { + // check if client clearly asks for mocked response + $pingHeader = 'X-{{invokerPackage}}-Mock'; + $pingHeaderCode = 'X-{{invokerPackage}}-Mock-Code'; + if ( + $request->hasHeader($pingHeader) + && $request->getHeader($pingHeader)[0] === 'ping' + ) { + $responses = (array) $responses; + $requestedResponseCode = ($request->hasHeader($pingHeaderCode)) ? $request->getHeader($pingHeaderCode)[0] : 'default'; + if (array_key_exists($requestedResponseCode, $responses)) { + return $requestedResponseCode; + } + + // return first response key + reset($responses); + return key($responses); + } + + return false; + }; + }, + 'mocker.afterCallback' => function () { + return function (\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response) { + // mark mocked response to distinguish real and fake responses + return $response->withHeader('X-{{invokerPackage}}-Mock', 'pong'); + }; + }, ]; diff --git a/modules/openapi-generator/src/main/resources/php-slim4-server/register_dependencies.mustache b/modules/openapi-generator/src/main/resources/php-slim4-server/register_dependencies.mustache index 14e1d65f884f..732325d09e00 100644 --- a/modules/openapi-generator/src/main/resources/php-slim4-server/register_dependencies.mustache +++ b/modules/openapi-generator/src/main/resources/php-slim4-server/register_dependencies.mustache @@ -59,6 +59,15 @@ final class RegisterDependencies \DI\get('pdo.password'), \DI\get('pdo.options', null) ), + + // DataMocker + // @see https://github.com/ybelenko/openapi-data-mocker-server-middleware + \OpenAPIServer\Mock\OpenApiDataMockerInterface::class => \DI\create(\OpenAPIServer\Mock\OpenApiDataMocker::class) + ->method('setModelsNamespace', '{{modelPackage}}\\'), + + \OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory::class => \DI\autowire() + ->constructorParameter('getMockStatusCodeCallback', \DI\get('mocker.getMockStatusCodeCallback')) + ->constructorParameter('afterCallback', \DI\get('mocker.afterCallback')), ]); } } diff --git a/modules/openapi-generator/src/main/resources/php-slim4-server/register_routes.mustache b/modules/openapi-generator/src/main/resources/php-slim4-server/register_routes.mustache index 35bf34e3a0a0..8b1cbf98adae 100644 --- a/modules/openapi-generator/src/main/resources/php-slim4-server/register_routes.mustache +++ b/modules/openapi-generator/src/main/resources/php-slim4-server/register_routes.mustache @@ -116,6 +116,16 @@ class RegisterRoutes return $response; }); + // create mock middleware factory + /** @var \Psr\Container\ContainerInterface */ + $container = $app->getContainer(); + /** @var \OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory|null */ + $mockMiddlewareFactory = null; + if ($container->has(\OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory::class)) { + // I know, anti-pattern. Don't retrieve dependency directly from container + $mockMiddlewareFactory = $container->get(\OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory::class); + } + foreach ($this->operations as $operation) { $callback = function (ServerRequestInterface $request) use ($operation) { $message = "How about extending {$operation['classname']} by {$operation['apiPackage']}\\{$operation['userClassname']} class implementing {$operation['operationId']} as a {$operation['httpMethod']} method?"; @@ -129,6 +139,13 @@ class RegisterRoutes $callback = ["\\{$operation['apiPackage']}\\{$operation['userClassname']}", $operation['operationId']]; } + if ($mockMiddlewareFactory) { + $mockSchemaResponses = array_map(function ($item) { + return json_decode($item['jsonSchema'], true); + }, $operation['responses']); + $middlewares[] = $mockMiddlewareFactory->create($mockSchemaResponses); + } + $route = $app->map( [$operation['httpMethod']], "{$operation['basePathWithoutHost']}{$operation['path']}", From 9d64d83ce47ca494d467cd456f86d81cb499e873 Mon Sep 17 00:00:00 2001 From: Yuriy Belenko Date: Tue, 5 Apr 2022 05:59:15 +0300 Subject: [PATCH 2/2] Refresh samples --- samples/server/petstore/php-slim4/README.md | 16 +++++++++ .../server/petstore/php-slim4/composer.json | 2 +- .../php-slim4/config/dev/default.inc.php | 33 +++++++++++++++++++ .../lib/App/RegisterDependencies.php | 9 +++++ .../php-slim4/lib/App/RegisterRoutes.php | 17 ++++++++++ 5 files changed, 76 insertions(+), 1 deletion(-) diff --git a/samples/server/petstore/php-slim4/README.md b/samples/server/petstore/php-slim4/README.md index a99f4a13cc49..dc91b2bcbebd 100644 --- a/samples/server/petstore/php-slim4/README.md +++ b/samples/server/petstore/php-slim4/README.md @@ -98,6 +98,22 @@ Switch your app environment to development in `public/.htaccess` file: ``` +## Mock Server +Since this feature should be used for development only, change environment to `development` and send additional HTTP header `X-OpenAPIServer-Mock: ping` with any request to get mocked response. +CURL example: +```console +curl --request GET \ + --url 'http://localhost:8888/v2/pet/findByStatus?status=available' \ + --header 'accept: application/json' \ + --header 'X-OpenAPIServer-Mock: ping' +[{"id":-8738629417578509312,"category":{"id":-4162503862215270400,"name":"Lorem ipsum dol"},"name":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem i","photoUrls":["Lor"],"tags":[{"id":-3506202845849391104,"name":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectet"}],"status":"pending"}] +``` + +Used packages: +* [Openapi Data Mocker](https://github.com/ybelenko/openapi-data-mocker) - first implementation of OAS3 fake data generator. +* [Openapi Data Mocker Server Middleware](https://github.com/ybelenko/openapi-data-mocker-server-middleware) - PSR-15 HTTP server middleware. +* [Openapi Data Mocker Interfaces](https://github.com/ybelenko/openapi-data-mocker-interfaces) - package with mocking interfaces. + ## API Endpoints All URIs are relative to *http://petstore.swagger.io/v2* diff --git a/samples/server/petstore/php-slim4/composer.json b/samples/server/petstore/php-slim4/composer.json index 0e99d73f62c5..afb802fcc6bf 100644 --- a/samples/server/petstore/php-slim4/composer.json +++ b/samples/server/petstore/php-slim4/composer.json @@ -14,7 +14,7 @@ "php-di/slim-bridge": "^3.2", "slim/psr7": "^1.1.0", "ybelenko/openapi-data-mocker": "^1.0", - "ybelenko/openapi-data-mocker-server-middleware": "^1.0" + "ybelenko/openapi-data-mocker-server-middleware": "^1.2" }, "require-dev": { "overtrue/phplint": "^2.0.2", diff --git a/samples/server/petstore/php-slim4/config/dev/default.inc.php b/samples/server/petstore/php-slim4/config/dev/default.inc.php index 4bc496fc288d..b30088b9fca2 100644 --- a/samples/server/petstore/php-slim4/config/dev/default.inc.php +++ b/samples/server/petstore/php-slim4/config/dev/default.inc.php @@ -62,4 +62,37 @@ 'pdo.options' => [ \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, ], + + // mocker + // OBVIOUSLY MUST NOT BE USED for production + // @see https://github.com/ybelenko/openapi-data-mocker-server-middleware + 'mocker.getMockStatusCodeCallback' => function () { + return function (\Psr\Http\Message\ServerRequestInterface $request, array $responses) { + // check if client clearly asks for mocked response + $pingHeader = 'X-OpenAPIServer-Mock'; + $pingHeaderCode = 'X-OpenAPIServer-Mock-Code'; + if ( + $request->hasHeader($pingHeader) + && $request->getHeader($pingHeader)[0] === 'ping' + ) { + $responses = (array) $responses; + $requestedResponseCode = ($request->hasHeader($pingHeaderCode)) ? $request->getHeader($pingHeaderCode)[0] : 'default'; + if (array_key_exists($requestedResponseCode, $responses)) { + return $requestedResponseCode; + } + + // return first response key + reset($responses); + return key($responses); + } + + return false; + }; + }, + 'mocker.afterCallback' => function () { + return function (\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response) { + // mark mocked response to distinguish real and fake responses + return $response->withHeader('X-OpenAPIServer-Mock', 'pong'); + }; + }, ]; diff --git a/samples/server/petstore/php-slim4/lib/App/RegisterDependencies.php b/samples/server/petstore/php-slim4/lib/App/RegisterDependencies.php index 0cd54563ed99..137d2671a385 100644 --- a/samples/server/petstore/php-slim4/lib/App/RegisterDependencies.php +++ b/samples/server/petstore/php-slim4/lib/App/RegisterDependencies.php @@ -72,6 +72,15 @@ public function __invoke(\DI\ContainerBuilder $containerBuilder): void \DI\get('pdo.password'), \DI\get('pdo.options', null) ), + + // DataMocker + // @see https://github.com/ybelenko/openapi-data-mocker-server-middleware + \OpenAPIServer\Mock\OpenApiDataMockerInterface::class => \DI\create(\OpenAPIServer\Mock\OpenApiDataMocker::class) + ->method('setModelsNamespace', 'OpenAPIServer\Model\\'), + + \OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory::class => \DI\autowire() + ->constructorParameter('getMockStatusCodeCallback', \DI\get('mocker.getMockStatusCodeCallback')) + ->constructorParameter('afterCallback', \DI\get('mocker.afterCallback')), ]); } } diff --git a/samples/server/petstore/php-slim4/lib/App/RegisterRoutes.php b/samples/server/petstore/php-slim4/lib/App/RegisterRoutes.php index 4cb4f853ae11..0c719abdfb97 100644 --- a/samples/server/petstore/php-slim4/lib/App/RegisterRoutes.php +++ b/samples/server/petstore/php-slim4/lib/App/RegisterRoutes.php @@ -845,6 +845,16 @@ public function __invoke(\Slim\App $app): void return $response; }); + // create mock middleware factory + /** @var \Psr\Container\ContainerInterface */ + $container = $app->getContainer(); + /** @var \OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory|null */ + $mockMiddlewareFactory = null; + if ($container->has(\OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory::class)) { + // I know, anti-pattern. Don't retrieve dependency directly from container + $mockMiddlewareFactory = $container->get(\OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory::class); + } + foreach ($this->operations as $operation) { $callback = function (ServerRequestInterface $request) use ($operation) { $message = "How about extending {$operation['classname']} by {$operation['apiPackage']}\\{$operation['userClassname']} class implementing {$operation['operationId']} as a {$operation['httpMethod']} method?"; @@ -858,6 +868,13 @@ public function __invoke(\Slim\App $app): void $callback = ["\\{$operation['apiPackage']}\\{$operation['userClassname']}", $operation['operationId']]; } + if ($mockMiddlewareFactory) { + $mockSchemaResponses = array_map(function ($item) { + return json_decode($item['jsonSchema'], true); + }, $operation['responses']); + $middlewares[] = $mockMiddlewareFactory->create($mockSchemaResponses); + } + $route = $app->map( [$operation['httpMethod']], "{$operation['basePathWithoutHost']}{$operation['path']}",