Skip to content

Commit

Permalink
Up phpstan level to 6 and added new response types.
Browse files Browse the repository at this point in the history
  • Loading branch information
roquie committed May 2, 2020
1 parent 5cb050d commit 9f37b9d
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
key: v1-dependencies-{{ checksum "composer.json" }}

# run static analyse
- run: vendor/bin/phpstan analyse src --level 5
- run: vendor/bin/phpstan analyse src --level 6

# run tests!
- run: vendor/bin/phpunit --coverage-clover=coverage.xml
Expand Down
13 changes: 13 additions & 0 deletions src/Exception/ValidationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ final class ValidationException extends HttpException
public const VALIDATION_CODE = 'ValidationError';
public const VALIDATION_MESSAGE = 'Error occurred in input data. Please, correct them and send request again.';

/**
* @var array<string, array>
*/
private array $errors;

/**
* @param array<string, array> $errors
* @return static
*/
public static function invalidBodyContents(array $errors): self
{
$exception = new static(self::VALIDATION_MESSAGE, Status::UNPROCESSABLE_ENTITY, self::VALIDATION_CODE);
Expand All @@ -19,11 +26,17 @@ public static function invalidBodyContents(array $errors): self
return $exception;
}

/**
* @param array<string, array> $errors
*/
public function setErrors(array $errors): void
{
$this->errors = $errors;
}

/**
* @return array<string, array>
*/
public function getErrors(): array
{
return $this->errors;
Expand Down
1 change: 1 addition & 0 deletions src/Handler/HealthcheckHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class HealthcheckHandler implements RequestHandler
{
public function handleRequest(Request $request): Promise
{
// @phpstan-ignore-next-line
return call(fn () => new Response(Status::OK, [], 'ok'));
}
}
4 changes: 2 additions & 2 deletions src/Handler/TrustedRequestInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ interface TrustedRequestInterface extends RequestHandler
/**
* Sets the trusted body contents after validation.
*
* @param array $body
* @param array<mixed> $body
* @return $this
*/
public function setTrustedBody(array $body): self;

/**
* Gets the trusted body contents after validation.
*
* @return array
* @return array<mixed>
*/
public function getTrustedBody(): array;
}
7 changes: 5 additions & 2 deletions src/Handler/TrustedRequestTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@

trait TrustedRequestTrait
{
/**
* @var array<mixed>
*/
protected array $trustedBody;

/**
* Sets the trusted body contents after validation.
*
* @param array $body
* @param array<mixed> $body
* @return $this
*/
public function setTrustedBody(array $body): self
Expand All @@ -24,7 +27,7 @@ public function setTrustedBody(array $body): self
/**
* Gets the trusted body contents after validation.
*
* @return array
* @return array<mixed>
*/
public function getTrustedBody(): array
{
Expand Down
9 changes: 5 additions & 4 deletions src/Middleware/AcceptJsonBody.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@
abstract class AcceptJsonBody extends AbstractBody
{
/**
* @param \Amp\Http\Server\Request $request
* @param \Amp\Http\Server\RequestHandler $requestHandler
* @return \Amp\Promise
* @throws \Spacetab\AmphpSupport\Exception\JsonRequestException
* @param Request $request
* @param RequestHandler $requestHandler
* @return Promise<\Amp\Http\Server\Response>
* @throws JsonRequestException
*/
public function handleRequest(Request $request, RequestHandler $requestHandler): Promise
{
$this->requestHasValidHeader($request);

// @phpstan-ignore-next-line
return call(function() use ($request, $requestHandler) {
$body = yield $request->getBody()->read();

Expand Down
15 changes: 14 additions & 1 deletion src/Middleware/ExceptionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,29 @@

final class ExceptionMiddleware implements Middleware
{
/**
* @var array<string, string>
*/
private array $validationMap;

/**
* ExceptionMiddleware constructor.
*
* @param array $validationMap
* @param array<string, string> $validationMap
*/
public function __construct(array $validationMap = [])
{
$this->validationMap = $validationMap;
}

/**
* @param Request $request
* @param RequestHandler $requestHandler
* @return Promise<\Amp\Http\Server\Response>
*/
public function handleRequest(Request $request, RequestHandler $requestHandler): Promise
{
// @phpstan-ignore-next-line
return call(function () use ($request, $requestHandler) {
try {
return yield $requestHandler->handleRequest($request);
Expand All @@ -49,6 +58,10 @@ public function handleRequest(Request $request, RequestHandler $requestHandler):
});
}

/**
* @param array<string, array<\HarmonyIO\Validation\Result\Error>> $errors
* @return \Spacetab\Transformation\ValidationError
*/
protected function fillUserMistakes(array $errors): ValidationError
{
$valid = new ValidationError();
Expand Down
8 changes: 6 additions & 2 deletions src/Server/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
use Amp\Http\Status;
use Amp\Promise;
use Amp\Success;
use Spacetab\AmphpSupport\Server\Response;
use Spacetab\Transformation\ErrorTransformation;

final class ErrorHandler implements ErrorHandlerInterface
{
/** {@inheritdoc} */
/**
* @param int $statusCode
* @param string|null $reason
* @param Request|null $request
* @return Promise<\Amp\Http\Server\Response>
*/
public function handleError(int $statusCode, string $reason = null, Request $request = null): Promise
{
return new Success(
Expand Down
36 changes: 35 additions & 1 deletion src/Server/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Spacetab\AmphpSupport\Server;

use Amp\ByteStream\InputStream;
use Amp\Http\Server\Response as ServerResponse;
use Amp\Http\Status;
use JsonException;
Expand Down Expand Up @@ -34,12 +35,45 @@ public static function asJson(TransformationInterface $value, int $status = Stat
}

/**
* @param array $value
* @param InputStream|string|null $stringOrStream
* @param string|null $filename
* @return ServerResponse
*/
public static function asPdf($stringOrStream, string $filename = null): ServerResponse
{
if (is_null($filename)) {
$filename = self::getFilename('pdf');
}

return new ServerResponse(Status::OK, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => "attachment; filename=\"{$filename}\""
], $stringOrStream);
}

/**
* @param InputStream|string|null $stringOrStream
* @return ServerResponse
*/
public static function asHtml($stringOrStream): ServerResponse
{
return new ServerResponse(Status::OK, [
'Content-Type' => 'text/html'
], $stringOrStream);
}

/**
* @param mixed $value
* @return string
* @throws \JsonException
*/
private static function jsonEncode($value): string
{
return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
}

private static function getFilename(string $extension): string
{
return sprintf("file-%s.%s", date('dmY-His'), $extension);
}
}
56 changes: 56 additions & 0 deletions tests/Integration/ComplexTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ public function handleRequest(Request $request): Promise {
}
}, $validationMiddleware);
$router->addRoute('POST', '/bad', new CallableRequestHandler(fn() => new Success), $validationMiddleware);
$router->addRoute('POST', '/pdf-file-passed', new CallableRequestHandler(fn() => Response::asPdf('pdf', 'file.pdf')));
$router->addRoute('POST', '/pdf-file-default', new CallableRequestHandler(fn() => Response::asPdf('pdf')));
$router->addRoute('POST', '/html', new CallableRequestHandler(fn() => Response::asHtml('<p>hi</p>')));
$this->server = new HttpServer($sockets, $router, new NullLogger());
$this->server->setErrorHandler(new ErrorHandler());

Expand Down Expand Up @@ -198,4 +201,57 @@ public function testHowToServerSendsUserGoAwayIfRequestHandlerNotTrusted()
$this->assertJsonStringEqualsJsonString($expected, $contents);
$this->assertSame(Status::INTERNAL_SERVER_ERROR, $response->getStatus());
}

public function testWhereServerResponseAsPdfFileWithPassedFilename()
{
call(fn () => yield $this->server->start());

$request = new ClientRequest(self::ADDRESS . '/pdf-file-passed', 'POST');

/** @var \Amp\Http\Client\Response $response */
$response = yield $this->client->request($request);
$contents = yield $response->getBody()->read();
$headers = $response->getHeaders();
yield $this->server->stop();

$this->assertSame(Status::OK, $response->getStatus());
$this->assertSame('application/pdf', $headers['content-type'][0]);
$this->assertSame('attachment; filename="file.pdf"', $headers['content-disposition'][0]);
$this->assertSame('pdf', $contents);
}

public function testWhereServerResponseAsPdfFileWithDefaultFilename()
{
call(fn () => yield $this->server->start());

$request = new ClientRequest(self::ADDRESS . '/pdf-file-default', 'POST');

/** @var \Amp\Http\Client\Response $response */
$response = yield $this->client->request($request);
$contents = yield $response->getBody()->read();
$headers = $response->getHeaders();
yield $this->server->stop();

$this->assertSame(Status::OK, $response->getStatus());
$this->assertSame('application/pdf', $headers['content-type'][0]);
$this->assertMatchesRegularExpression('/attachment\; filename\=\".*\"/', $headers['content-disposition'][0]);
$this->assertSame('pdf', $contents);
}

public function testWhereServerResponseAsHtml()
{
call(fn () => yield $this->server->start());

$request = new ClientRequest(self::ADDRESS . '/html', 'POST');

/** @var \Amp\Http\Client\Response $response */
$response = yield $this->client->request($request);
$contents = yield $response->getBody()->read();
$headers = $response->getHeaders();
yield $this->server->stop();

$this->assertSame(Status::OK, $response->getStatus());
$this->assertSame('text/html', $headers['content-type'][0]);
$this->assertSame('<p>hi</p>', $contents);
}
}

0 comments on commit 9f37b9d

Please sign in to comment.