[php-slim4] Add Mock Server (#12044)

* Setup Data Mocker as dev dependency

New 1.2.0 version of the package contains required factory class.

* Refresh samples
This commit is contained in:
Yuriy Belenko
2022-04-09 04:43:07 +03:00
committed by GitHub
parent 97eca73160
commit cd56a4b1a1
10 changed files with 152 additions and 2 deletions

View File

@@ -115,6 +115,22 @@ Switch your app environment to development in `public/.htaccess` file:
</IfModule>
```
## 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

View File

@@ -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",

View File

@@ -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');
};
},
];

View File

@@ -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')),
]);
}
}

View File

@@ -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']}",

View File

@@ -98,6 +98,22 @@ Switch your app environment to development in `public/.htaccess` file:
</IfModule>
```
## 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*

View File

@@ -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",

View File

@@ -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-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');
};
},
];

View File

@@ -72,6 +72,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', 'OpenAPIServer\Model\\'),
\OpenAPIServer\Mock\OpenApiDataMockerRouteMiddlewareFactory::class => \DI\autowire()
->constructorParameter('getMockStatusCodeCallback', \DI\get('mocker.getMockStatusCodeCallback'))
->constructorParameter('afterCallback', \DI\get('mocker.afterCallback')),
]);
}
}

View File

@@ -845,6 +845,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?";
@@ -858,6 +868,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']}",