From 73fe94562be290f55b106fc1cb315947711340cc Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Mon, 29 Jul 2024 12:51:25 +0200 Subject: [PATCH 1/9] Schema validation and error detail settings by get params --- CHANGELOG.md | 1 + README.md | 7 +++++++ src/Presenters/ApiPresenter.php | 8 +++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67dc7df..d96f7bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip #### Added * Button to copy `Body` content in api console +* Ability to disable schema validation and provide additional error info with get parameters. #### Changed * Handler tag wrapper has changed class from `btn` to `label` diff --git a/README.md b/README.md index 7f321ba..160ea14 100644 --- a/README.md +++ b/README.md @@ -561,6 +561,13 @@ Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recen $ composer test ``` +### Disable schema validation in not production environment +Include get parameter no_schema_validate in your request to disable schema validation. This is useful for testing purposes. +* schema validation is disabled by default in production environment for performance reasons + +### Add additional info to error response +Include get parameter error_detail in your request to show additional info in error response. This is useful for debugging purposes. + ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details. diff --git a/src/Presenters/ApiPresenter.php b/src/Presenters/ApiPresenter.php index b3190f5..64a91d5 100644 --- a/src/Presenters/ApiPresenter.php +++ b/src/Presenters/ApiPresenter.php @@ -69,6 +69,7 @@ public function run(Request $request): IResponse $handler = $api->getHandler(); $authorization = $api->getAuthorization(); $rateLimit = $api->getRateLimit(); + $getParams = $request->getParameters(); $authResponse = $this->checkAuth($authorization); if ($authResponse !== null) { @@ -83,7 +84,7 @@ public function run(Request $request): IResponse $paramsProcessor = new ParamsProcessor($handler->params()); if ($paramsProcessor->isError()) { $this->response->setCode(Response::S400_BAD_REQUEST); - if (!Debugger::$productionMode) { + if (!Debugger::$productionMode || isset($getParams['error_detail'])) { $response = new JsonResponse(['status' => 'error', 'message' => 'wrong input', 'detail' => $paramsProcessor->getErrors()]); } else { $response = new JsonResponse(['status' => 'error', 'message' => 'wrong input']); @@ -94,7 +95,8 @@ public function run(Request $request): IResponse try { $response = $handler->handle($params); $code = $response->getCode(); - if (!Debugger::$productionMode) { /// If not production mode, validate output + + if (!Debugger::$productionMode && !isset($getParams['no_schema_validate'])) { /// If not production mode or no no_schema_validate parameter present, validate output $outputValid = count($handler->outputs()) === 0; // back compatibility for handlers with no outputs defined $outputValidatorErrors = []; foreach ($handler->outputs() as $output) { @@ -115,7 +117,7 @@ public function run(Request $request): IResponse } } } catch (Throwable $exception) { - if (!Debugger::$productionMode) { + if (!Debugger::$productionMode || isset($getParams['error_detail'])) { $response = new JsonApiResponse(Response::S500_INTERNAL_SERVER_ERROR, ['status' => 'error', 'message' => 'Internal server error', 'detail' => $exception->getMessage()]); } else { $response = new JsonApiResponse(Response::S500_INTERNAL_SERVER_ERROR, ['status' => 'error', 'message' => 'Internal server error']); From 03aa8148b6e95d7ffe43bdf11530d8dbc7edc3d4 Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Thu, 1 Aug 2024 09:35:13 +0200 Subject: [PATCH 2/9] Schema validation and error detail settings by get params --- CHANGELOG.md | 1 + README.md | 16 ++++----- .../Configurator/ConfiguratorInterface.php | 14 ++++++++ .../Configurator/DebuggerConfigurator.php | 21 +++++++++++ src/Output/Configurator/EnvConfigurator.php | 28 +++++++++++++++ src/Output/Configurator/QueryConfigurator.php | 36 +++++++++++++++++++ src/Presenters/ApiPresenter.php | 11 +++--- 7 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 src/Output/Configurator/ConfiguratorInterface.php create mode 100644 src/Output/Configurator/DebuggerConfigurator.php create mode 100644 src/Output/Configurator/EnvConfigurator.php create mode 100644 src/Output/Configurator/QueryConfigurator.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d96f7bc..f0ae678 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip * Support for semantic versioning api. * [BC] DefaultHandler response code 404 instead 400 * [BC] Added Container to API Decider +* [BC] Output Configurator, Allows different methods for output configuration. Needs to be added to config services. #### Added * CorsPreflightHandlerInterface - resolve multiple service registered handler error diff --git a/README.md b/README.md index 160ea14..af54942 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Library is compliant with [PSR-1][], [PSR-2][], [PSR-3][] and [PSR-4][]. ## How Nette-API works -First, you have to register library presenter for routing. In *config.neon* just add this line: +First, you have to register library presenter for routingz. In *config.neon* just add this line: ```neon application: @@ -40,6 +40,13 @@ application: Api: Tomaj\NetteApi\Presenters\*Presenter ``` +Then register your preffered output configurator in *config.neon* services: + +```neon +services: + apiOutputConfigurator: Tomaj\NetteApi\Output\Configurator\QueryConfigurator +``` + And add route to you RouterFactory: ```php @@ -561,13 +568,6 @@ Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recen $ composer test ``` -### Disable schema validation in not production environment -Include get parameter no_schema_validate in your request to disable schema validation. This is useful for testing purposes. -* schema validation is disabled by default in production environment for performance reasons - -### Add additional info to error response -Include get parameter error_detail in your request to show additional info in error response. This is useful for debugging purposes. - ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details. diff --git a/src/Output/Configurator/ConfiguratorInterface.php b/src/Output/Configurator/ConfiguratorInterface.php new file mode 100644 index 0000000..4c9453a --- /dev/null +++ b/src/Output/Configurator/ConfiguratorInterface.php @@ -0,0 +1,14 @@ +getParameters(); + return !isset($getParams['no_schema_validate']); + } + + public function showErrorDetail(?Request $request = null): bool + { + if ($request === null) { + return false; + } + $getParams = $request->getParameters(); + return isset($getParams['error_detail']); + } +} \ No newline at end of file diff --git a/src/Presenters/ApiPresenter.php b/src/Presenters/ApiPresenter.php index 64a91d5..44d2e80 100644 --- a/src/Presenters/ApiPresenter.php +++ b/src/Presenters/ApiPresenter.php @@ -21,6 +21,7 @@ use Tomaj\NetteApi\RateLimit\RateLimitInterface; use Tomaj\NetteApi\Response\JsonApiResponse; use Tracy\Debugger; +use Tomaj\NetteApi\Output\Configurator\ConfiguratorInterface; final class ApiPresenter implements IPresenter { @@ -33,6 +34,9 @@ final class ApiPresenter implements IPresenter /** @var Container @inject */ public $context; + /** @var ConfiguratorInterface @inject */ + public $outputConfigurator; + /** * CORS header settings * @@ -69,7 +73,6 @@ public function run(Request $request): IResponse $handler = $api->getHandler(); $authorization = $api->getAuthorization(); $rateLimit = $api->getRateLimit(); - $getParams = $request->getParameters(); $authResponse = $this->checkAuth($authorization); if ($authResponse !== null) { @@ -84,7 +87,7 @@ public function run(Request $request): IResponse $paramsProcessor = new ParamsProcessor($handler->params()); if ($paramsProcessor->isError()) { $this->response->setCode(Response::S400_BAD_REQUEST); - if (!Debugger::$productionMode || isset($getParams['error_detail'])) { + if ($this->outputConfigurator->showErrorDetail($request)) { $response = new JsonResponse(['status' => 'error', 'message' => 'wrong input', 'detail' => $paramsProcessor->getErrors()]); } else { $response = new JsonResponse(['status' => 'error', 'message' => 'wrong input']); @@ -96,7 +99,7 @@ public function run(Request $request): IResponse $response = $handler->handle($params); $code = $response->getCode(); - if (!Debugger::$productionMode && !isset($getParams['no_schema_validate'])) { /// If not production mode or no no_schema_validate parameter present, validate output + if ($this->outputConfigurator->validateSchema($request)) { /// If not production mode or no no_schema_validate parameter present, validate output $outputValid = count($handler->outputs()) === 0; // back compatibility for handlers with no outputs defined $outputValidatorErrors = []; foreach ($handler->outputs() as $output) { @@ -117,7 +120,7 @@ public function run(Request $request): IResponse } } } catch (Throwable $exception) { - if (!Debugger::$productionMode || isset($getParams['error_detail'])) { + if ($this->outputConfigurator->showErrorDetail($request)) { $response = new JsonApiResponse(Response::S500_INTERNAL_SERVER_ERROR, ['status' => 'error', 'message' => 'Internal server error', 'detail' => $exception->getMessage()]); } else { $response = new JsonApiResponse(Response::S500_INTERNAL_SERVER_ERROR, ['status' => 'error', 'message' => 'Internal server error']); From 3c7a25fcda03eed9574849943d19e1c77de70cbe Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Thu, 1 Aug 2024 09:35:51 +0200 Subject: [PATCH 3/9] Schema validation and error detail settings by get params --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af54942..1e20ab8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Library is compliant with [PSR-1][], [PSR-2][], [PSR-3][] and [PSR-4][]. ## How Nette-API works -First, you have to register library presenter for routingz. In *config.neon* just add this line: +First, you have to register library presenter for routing. In *config.neon* just add this line: ```neon application: From d6d78bf3c0901df91251eedd3e6e7415b1acecaf Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Fri, 2 Aug 2024 10:15:07 +0200 Subject: [PATCH 4/9] Constructor options --- README.md | 2 +- src/Output/Configurator/EnvConfigurator.php | 23 +++++++++++++---- src/Output/Configurator/QueryConfigurator.php | 25 +++++++++++-------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1e20ab8..f164740 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Then register your preffered output configurator in *config.neon* services: ```neon services: - apiOutputConfigurator: Tomaj\NetteApi\Output\Configurator\QueryConfigurator + apiOutputConfigurator: Tomaj\NetteApi\Output\Configurator\DebuggerConfigurator ``` And add route to you RouterFactory: diff --git a/src/Output/Configurator/EnvConfigurator.php b/src/Output/Configurator/EnvConfigurator.php index cfc495c..cd8bdfb 100644 --- a/src/Output/Configurator/EnvConfigurator.php +++ b/src/Output/Configurator/EnvConfigurator.php @@ -8,10 +8,23 @@ class EnvConfigurator implements ConfiguratorInterface { + private string $envVariable = "APP_ENV"; + private string $productionValue = "production"; + + /** + * @param string $envVariable Which environment variable to check for production value + * @param string $productionValue Value that indicates production environment eg. "production" or "prod"... + */ + public function __construct(string $envVariable = "APP_ENV", string $productionValue = "production") + { + $this->envVariable = $envVariable; + $this->productionValue = $productionValue; + } + public function validateSchema(?Request $request = null): bool { - $appEnv = getenv("APP_ENV"); - if ($appEnv === "production") { + $appEnv = getenv($this->envVariable); + if ($appEnv === $this->productionValue) { return false; } return true; @@ -19,10 +32,10 @@ public function validateSchema(?Request $request = null): bool public function showErrorDetail(?Request $request = null): bool { - $appEnv = getenv("APP_ENV"); - if ($appEnv === "production") { + $appEnv = getenv($this->envVariable); + if ($appEnv === $this->productionValue) { return false; } return true; } -} \ No newline at end of file +} diff --git a/src/Output/Configurator/QueryConfigurator.php b/src/Output/Configurator/QueryConfigurator.php index afeb32d..c66964b 100644 --- a/src/Output/Configurator/QueryConfigurator.php +++ b/src/Output/Configurator/QueryConfigurator.php @@ -8,21 +8,26 @@ class QueryConfigurator implements ConfiguratorInterface { - /* - ### Disable schema validation in not production environment - Include get parameter no_schema_validate in your request to disable schema validation. This is useful for testing purposes. - * schema validation is disabled by default in production environment for performance reasons + private string $noSchemaValidateParam = "no_schema_validate"; + private string $errorDetailParam = "error_detail"; + + /** + * @param string $noSchemaValidateParam Name of get parameter to disable schema validation + * @param string $errorDetailParam Name of get parameter to show additional info in error response + */ + public function __construct(string $noSchemaValidateParam = "no_schema_validate", string $errorDetailParam = "error_detail") + { + $this->noSchemaValidateParam = $noSchemaValidateParam; + $this->errorDetailParam = $errorDetailParam; + } - ### Add additional info to error response - Include get parameter error_detail in your request to show additional info in error response. This is useful for debugging purposes. - */ public function validateSchema(?Request $request = null): bool { if ($request === null) { return false; } $getParams = $request->getParameters(); - return !isset($getParams['no_schema_validate']); + return !isset($getParams[$this->noSchemaValidateParam]); } public function showErrorDetail(?Request $request = null): bool @@ -31,6 +36,6 @@ public function showErrorDetail(?Request $request = null): bool return false; } $getParams = $request->getParameters(); - return isset($getParams['error_detail']); + return isset($getParams[$this->errorDetailParam]); } -} \ No newline at end of file +} From 3ac7645f1c945b0811bb5c8d92ac7ae6e688947d Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Fri, 2 Aug 2024 10:16:16 +0200 Subject: [PATCH 5/9] comment remove --- src/Presenters/ApiPresenter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presenters/ApiPresenter.php b/src/Presenters/ApiPresenter.php index 44d2e80..797611e 100644 --- a/src/Presenters/ApiPresenter.php +++ b/src/Presenters/ApiPresenter.php @@ -99,7 +99,7 @@ public function run(Request $request): IResponse $response = $handler->handle($params); $code = $response->getCode(); - if ($this->outputConfigurator->validateSchema($request)) { /// If not production mode or no no_schema_validate parameter present, validate output + if ($this->outputConfigurator->validateSchema($request)) { $outputValid = count($handler->outputs()) === 0; // back compatibility for handlers with no outputs defined $outputValidatorErrors = []; foreach ($handler->outputs() as $output) { From 2733bd5b61ed55ed5fab2fdb8eeb624297d83722 Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Fri, 2 Aug 2024 10:17:15 +0200 Subject: [PATCH 6/9] sniffer --- src/Output/Configurator/ConfiguratorInterface.php | 2 +- src/Output/Configurator/DebuggerConfigurator.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Output/Configurator/ConfiguratorInterface.php b/src/Output/Configurator/ConfiguratorInterface.php index 4c9453a..01c817e 100644 --- a/src/Output/Configurator/ConfiguratorInterface.php +++ b/src/Output/Configurator/ConfiguratorInterface.php @@ -11,4 +11,4 @@ interface ConfiguratorInterface public function validateSchema(?Request $request = null): bool; public function showErrorDetail(?Request $request = null): bool; -} \ No newline at end of file +} diff --git a/src/Output/Configurator/DebuggerConfigurator.php b/src/Output/Configurator/DebuggerConfigurator.php index 89be75e..6315210 100644 --- a/src/Output/Configurator/DebuggerConfigurator.php +++ b/src/Output/Configurator/DebuggerConfigurator.php @@ -18,4 +18,4 @@ public function showErrorDetail(?Request $request = null): bool { return !Debugger::$productionMode; } -} \ No newline at end of file +} From fe6f74fc19fdbc2d7a8d75572696c9013664aebd Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Fri, 2 Aug 2024 10:20:06 +0200 Subject: [PATCH 7/9] unit test --- tests/Presenters/ApiPresenterTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/Presenters/ApiPresenterTest.php b/tests/Presenters/ApiPresenterTest.php index 5eca87b..804ace4 100644 --- a/tests/Presenters/ApiPresenterTest.php +++ b/tests/Presenters/ApiPresenterTest.php @@ -4,12 +4,10 @@ namespace Tomaj\NetteApi\Test\Presenters; -use PHPUnit\Framework\TestCase; use Nette\Application\Request; use Nette\DI\Container; use Nette\Http\Response as HttpResponse; -use Nette\Http\Request as HttpRequest; -use Nette\Http\UrlScript; +use PHPUnit\Framework\TestCase; use Tomaj\NetteApi\ApiDecider; use Tomaj\NetteApi\Authorization\BearerTokenAuthorization; use Tomaj\NetteApi\Authorization\NoAuthorization; @@ -18,6 +16,7 @@ use Tomaj\NetteApi\Handlers\EchoHandler; use Tomaj\NetteApi\Misc\IpDetector; use Tomaj\NetteApi\Misc\StaticTokenRepository; +use Tomaj\NetteApi\Output\Configurator\DebuggerConfigurator; use Tomaj\NetteApi\Presenters\ApiPresenter; use Tomaj\NetteApi\Test\Handler\TestHandler; use Tracy\Debugger; @@ -41,6 +40,7 @@ public function testSimpleResponse() $presenter->apiDecider = $apiDecider; $presenter->response = new HttpResponse(); $presenter->context = new Container(); + $presenter->outputConfigurator = new DebuggerConfigurator(); $request = new Request('Api:Api:default', 'GET', ['version' => '1', 'package' => 'test', 'apiAction' => 'api']); $result = $presenter->run($request); @@ -64,6 +64,7 @@ public function testWithAuthorization() $presenter->apiDecider = $apiDecider; $presenter->response = new HttpResponse(); $presenter->context = new Container(); + $presenter->outputConfigurator = new DebuggerConfigurator(); $request = new Request('Api:Api:default', 'GET', ['version' => '1', 'package' => 'test', 'apiAction' => 'api']); $result = $presenter->run($request); @@ -85,6 +86,7 @@ public function testWithParams() $presenter->apiDecider = $apiDecider; $presenter->response = new HttpResponse(); $presenter->context = new Container(); + $presenter->outputConfigurator = new DebuggerConfigurator(); Debugger::$productionMode = Debugger::PRODUCTION; @@ -114,6 +116,7 @@ public function testWithOutputs() $presenter->apiDecider = $apiDecider; $presenter->response = new HttpResponse(); $presenter->context = new Container(); + $presenter->outputConfigurator = new DebuggerConfigurator(); $request = new Request('Api:Api:default', 'GET', ['version' => '1', 'package' => 'test', 'apiAction' => 'api']); $result = $presenter->run($request); From e567c172b22fbaa50bfe16644506777b78636e2a Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Fri, 2 Aug 2024 10:22:46 +0200 Subject: [PATCH 8/9] syntax checker --- src/Output/Configurator/EnvConfigurator.php | 4 ++-- src/Output/Configurator/QueryConfigurator.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Output/Configurator/EnvConfigurator.php b/src/Output/Configurator/EnvConfigurator.php index cd8bdfb..fb8f635 100644 --- a/src/Output/Configurator/EnvConfigurator.php +++ b/src/Output/Configurator/EnvConfigurator.php @@ -8,8 +8,8 @@ class EnvConfigurator implements ConfiguratorInterface { - private string $envVariable = "APP_ENV"; - private string $productionValue = "production"; + private $envVariable = "APP_ENV"; + private $productionValue = "production"; /** * @param string $envVariable Which environment variable to check for production value diff --git a/src/Output/Configurator/QueryConfigurator.php b/src/Output/Configurator/QueryConfigurator.php index c66964b..520cedb 100644 --- a/src/Output/Configurator/QueryConfigurator.php +++ b/src/Output/Configurator/QueryConfigurator.php @@ -8,8 +8,8 @@ class QueryConfigurator implements ConfiguratorInterface { - private string $noSchemaValidateParam = "no_schema_validate"; - private string $errorDetailParam = "error_detail"; + private $noSchemaValidateParam = "no_schema_validate"; + private $errorDetailParam = "error_detail"; /** * @param string $noSchemaValidateParam Name of get parameter to disable schema validation From 003fdc0619dab134ab70962b599407d4ef33c9c9 Mon Sep 17 00:00:00 2001 From: Martin Beranek Date: Tue, 13 Aug 2024 08:48:48 +0200 Subject: [PATCH 9/9] Fix " --- src/Output/Configurator/EnvConfigurator.php | 8 ++++---- src/Output/Configurator/QueryConfigurator.php | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Output/Configurator/EnvConfigurator.php b/src/Output/Configurator/EnvConfigurator.php index fb8f635..067b274 100644 --- a/src/Output/Configurator/EnvConfigurator.php +++ b/src/Output/Configurator/EnvConfigurator.php @@ -8,14 +8,14 @@ class EnvConfigurator implements ConfiguratorInterface { - private $envVariable = "APP_ENV"; - private $productionValue = "production"; + private $envVariable = 'APP_ENV'; + private $productionValue = 'production'; /** * @param string $envVariable Which environment variable to check for production value - * @param string $productionValue Value that indicates production environment eg. "production" or "prod"... + * @param string $productionValue Value that indicates production environment eg. 'production' or 'prod'... */ - public function __construct(string $envVariable = "APP_ENV", string $productionValue = "production") + public function __construct(string $envVariable = 'APP_ENV', string $productionValue = 'production') { $this->envVariable = $envVariable; $this->productionValue = $productionValue; diff --git a/src/Output/Configurator/QueryConfigurator.php b/src/Output/Configurator/QueryConfigurator.php index 520cedb..97efcf4 100644 --- a/src/Output/Configurator/QueryConfigurator.php +++ b/src/Output/Configurator/QueryConfigurator.php @@ -8,14 +8,14 @@ class QueryConfigurator implements ConfiguratorInterface { - private $noSchemaValidateParam = "no_schema_validate"; - private $errorDetailParam = "error_detail"; + private $noSchemaValidateParam = 'no_schema_validate'; + private $errorDetailParam = 'error_detail'; /** * @param string $noSchemaValidateParam Name of get parameter to disable schema validation * @param string $errorDetailParam Name of get parameter to show additional info in error response */ - public function __construct(string $noSchemaValidateParam = "no_schema_validate", string $errorDetailParam = "error_detail") + public function __construct(string $noSchemaValidateParam = 'no_schema_validate', string $errorDetailParam = 'error_detail') { $this->noSchemaValidateParam = $noSchemaValidateParam; $this->errorDetailParam = $errorDetailParam;