From f2c281934a2a2111b01b9048464ca36938573ae4 Mon Sep 17 00:00:00 2001 From: Pierre PLAZANET Date: Wed, 4 Nov 2020 14:49:59 +0100 Subject: [PATCH] feat: return 415 response --- composer.json | 1 + composer.lock | 48 +++++++- features/FeatureContext.php | 30 +++-- features/tests.feature | 9 ++ index.php | 211 +++++++++++++++++++----------------- 5 files changed, 191 insertions(+), 108 deletions(-) diff --git a/composer.json b/composer.json index 064fa73..9752f2e 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "name": "pedrotroller/http-markup", "require": { + "react/promise": "^2.8", "slim/slim": "^3.11", "symfony/process": "^5.1" }, diff --git a/composer.lock b/composer.lock index 13adf4c..c7ebd66 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3cf1910af816fbad0a5c9660259a37ca", + "content-hash": "47e8fc8ac481639dba08798ffafc1035", "packages": [ { "name": "container-interop/container-interop", @@ -233,6 +233,52 @@ ], "time": "2016-08-06T14:39:51+00:00" }, + { + "name": "react/promise", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "time": "2020-05-12T15:16:56+00:00" + }, { "name": "slim/slim", "version": "3.11.0", diff --git a/features/FeatureContext.php b/features/FeatureContext.php index 0786083..a0984e7 100644 --- a/features/FeatureContext.php +++ b/features/FeatureContext.php @@ -2,23 +2,21 @@ declare(strict_types=1); +use Behat\Behat\Tester\Exception\PendingException; use Behat\Behat\Context\Context; use Behat\Gherkin\Node\PyStringNode; use GuzzleHttp\Client; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\ServerException; +use Psr\Http\Message\ResponseInterface; use SebastianBergmann\Diff\Differ; use Webmozart\Assert\Assert; final class FeatureContext implements Context { - /** - * @var Client - */ - private $client; + private Client $client; - /** - * @var \GuzzleHttp\Psr7\Response|null - */ - private $response; + private ?ResponseInterface $response; public function __construct(string $host) { @@ -38,6 +36,7 @@ public function cleanup(): void */ public function iSendAMarkupFileWithContentTypeContaining(string $mimeType, PyStringNode $body): void { + try { $this->response = $this->client->request( 'POST', '/', @@ -50,6 +49,9 @@ public function iSendAMarkupFileWithContentTypeContaining(string $mimeType, PySt 'body' => (string) $body, ] ); + } catch (RequestException $requestException) { + $this->response = $requestException->getResponse(); + } } /** @@ -77,4 +79,16 @@ public function iShouldGetTheFollowingHtml(PyStringNode $html): void throw $exception; } } + + /** + * @Then I should get an unexpected media type http response + */ + public function iShouldGetAnUnexpectedMediaTypeHttpResponse(): void + { + if (null === $this->response) { + throw new Exception('No request sent.'); + } + + Assert::eq($this->response->getStatusCode(), 415); + } } diff --git a/features/tests.feature b/features/tests.feature index e9b5326..1a791ef 100644 --- a/features/tests.feature +++ b/features/tests.feature @@ -142,3 +142,12 @@ Feature: Parse markup file and transform them | mime type | | text/rst | | text/restructuredtext | + + Scenario: Push an unsupported media type + When I send a markup file with content type "application/json" containing + """ + { + "foo": "bar" + } + """ + Then I should get an unexpected media type http response diff --git a/index.php b/index.php index 94c2eab..d9f6266 100644 --- a/index.php +++ b/index.php @@ -5,112 +5,125 @@ use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Symfony\Component\Process\Process; +use Slim\App; +use React\Promise\Deferred; +use React\Promise\Promise; require_once __DIR__.'/vendor/autoload.php'; -$getExtensionFromRequest = function (Request $request): string { - $type = $request->getHeader('Content-Type'); - - if (empty($type)) { - throw new Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException( - 'No Content-Type provided' - ); - } - - if (is_array($type)) { - $type = current($type); - } - - $formats = [ - 'text/asciidoc' => 'asc', - 'text/creole' => 'creole', - 'text/markdown' => 'md', - 'text/org' => 'org', - 'text/orgmode' => 'org', - 'text/rdoc' => 'rdoc', - 'text/restructuredtext' => 'rst', - 'text/rst' => 'rst', - 'text/textile' => 'textile', - 'text/txstyle' => 'textile', - 'text/wiki' => 'wiki', +$app = new App(); + +$app->post('/', function (Request $request, Response $response, array $args): Response { + $result = [ + 'status' => 502, + 'body' => '', ]; - if (false === array_key_exists($type, $formats)) { - throw new Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException( - sprintf('Unsupported Content-Type. Only %s supported', implode(', ', array_keys($formats))) - ); - } - - return $formats[$type]; -}; - -$markupToHtml = function (string $markup, string $extension): string { - $temporaryFile = sprintf('%s/%s.%s', sys_get_temp_dir(), uniqid(), $extension); - $process = new Process( - [ - 'github-markup', - $temporaryFile, - ] - ); - - file_put_contents($temporaryFile, $markup); - $process->run(); - unlink($temporaryFile); - - if ($process->isSuccessful()) { - return $process->getOutput(); - } - - throw new Exception($process->getErrorOutput()); -}; - -$prettier = function (string $html): string { - $temporaryFile = sprintf('%s/%s.%s', sys_get_temp_dir(), uniqid(), 'html'); - $process = new Process( - [ - 'prettier', - '--write', - '--ignore-unknown', - '--parser', - 'html', - '--tab-width', - 2, - '--print-width', - 1000, - $temporaryFile, - ] - ); - - file_put_contents($temporaryFile, $html); - $process->run(); - $html = (string) file_get_contents($temporaryFile); - unlink($temporaryFile); - - if ($process->isSuccessful()) { - return $html; - } - - throw new Exception($process->getErrorOutput()); -}; - -$app = new \Slim\App(); - -$app->post('/', function (Request $request, Response $response, array $args) use ($getExtensionFromRequest, $markupToHtml, $prettier): void { - $extension = $getExtensionFromRequest($request); - $markup = (string) $request->getBody(); - - try { - $html = $markupToHtml($markup, $extension); - $html = $prettier($html); - $response->getBody()->write($html); - $response->withStatus(200); - } catch (Exception $exception) { - $response->withStatus(500); - } + $deferred = new Deferred(); + $promise = $deferred->promise(); + + $promise + ->then(function (Request $request): array { + $type = $request->getHeader('Content-Type'); + + if (empty($type)) { + throw new Exception('No Content-Type provided', 415); + } + + if (is_array($type)) { + $type = current($type); + } + + $formats = [ + 'text/asciidoc' => 'asc', + 'text/creole' => 'creole', + 'text/markdown' => 'md', + 'text/org' => 'org', + 'text/orgmode' => 'org', + 'text/rdoc' => 'rdoc', + 'text/restructuredtext' => 'rst', + 'text/rst' => 'rst', + 'text/textile' => 'textile', + 'text/txstyle' => 'textile', + 'text/wiki' => 'wiki', + ]; + + if (false === array_key_exists($type, $formats)) { + throw new Exception( + sprintf('Unsupported Content-Type. Only %s supported', implode(', ', array_keys($formats))), + 415, + ); + } + + return [$formats[$type], (string) $request->getBody()]; + }) + ->then(function (array $data): string { + [$extension, $markup] = $data; + $temporaryFile = sprintf('%s/%s.%s', sys_get_temp_dir(), uniqid(), $extension); + $process = new Process( + [ + 'github-markup', + $temporaryFile, + ] + ); + + file_put_contents($temporaryFile, $markup); + $process->run(); + unlink($temporaryFile); + + if ($process->isSuccessful()) { + return $process->getOutput(); + } + + throw new Exception($process->getErrorOutput()); + }) + ->then(function (string $html): string { + $temporaryFile = sprintf('%s/%s.%s', sys_get_temp_dir(), uniqid(), 'html'); + $process = new Process( + [ + 'prettier', + '--write', + '--ignore-unknown', + '--parser', + 'html', + '--tab-width', + 2, + '--print-width', + 1000, + $temporaryFile, + ] + ); + + file_put_contents($temporaryFile, $html); + $process->run(); + $html = (string) file_get_contents($temporaryFile); + unlink($temporaryFile); + + if ($process->isSuccessful()) { + return $html; + } + + throw new Exception($process->getErrorOutput()); + }) + ->otherwise(function (Exception $exception) use (&$result) { + $result['status'] = $exception->getCode() ?: 500; + $result['body'] = $exception->getMessage(); + }) + ->then(function (string $html) use (&$result): void { + $result['status'] = 200; + $result['body'] = $html; + }) + ; + + $deferred->resolve($request); + + $response->getBody()->write($result['body']); + + return $response->withStatus($result['status']); }); -$app->get('/_ping', function (Request $request, Response $response, array $args): void { - $response->withStatus(200); +$app->get('/_ping', function (Request $request, Response $response, array $args): Response { + return $response->withStatus(200); }); $app->run();