Skip to content

Commit

Permalink
Merge pull request #18 from sunrise-php/release/v1.6.0
Browse files Browse the repository at this point in the history
v1.6.0
  • Loading branch information
fenric authored Jul 27, 2020
2 parents ed81e40 + 131b6e0 commit cb64b9d
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 67 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ matrix:
fast_finish: true

before_install:
- travis_retry yes '' | pecl install -f apcu
- travis_retry composer self-update

install:
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"require": {
"php": "^7.1",
"doctrine/annotations": "^1.6",
"doctrine/cache": "^1.6",
"sunrise/http-router": "^2.4"
},
"require-dev": {
Expand Down
6 changes: 3 additions & 3 deletions src/AbstractAnnotation.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/**
* Import classes
*/
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Annotations\Reader as AnnotationReader;

/**
* Import functions
Expand All @@ -31,11 +31,11 @@ abstract class AbstractAnnotation extends AbstractObject
/**
* Recursively collects all annotations referenced by this object or its children
*
* @param SimpleAnnotationReader $annotationReader
* @param AnnotationReader $annotationReader
*
* @return ComponentObjectInterface[]
*/
public function getReferencedObjects(SimpleAnnotationReader $annotationReader) : array
public function getReferencedObjects(AnnotationReader $annotationReader) : array
{
$fields = $this->getFields();
$objects = [];
Expand Down
30 changes: 15 additions & 15 deletions src/AbstractAnnotationReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/**
* Import classes
*/
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Annotations\Reader as AnnotationReader;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
Expand Down Expand Up @@ -94,13 +94,13 @@ abstract public function getAnnotationName() : string;
/**
* Tries to find a referenced object that implements the `ComponentObjectInterface` interface
*
* @param SimpleAnnotationReader $annotationReader
* @param AnnotationReader $annotationReader
*
* @return ComponentObjectInterface
*
* @throws InvalidReferenceException
*/
public function getAnnotation(SimpleAnnotationReader $annotationReader) : ComponentObjectInterface
public function getAnnotation(AnnotationReader $annotationReader) : ComponentObjectInterface
{
$key = hash(
'md5',
Expand Down Expand Up @@ -128,17 +128,17 @@ public function getAnnotation(SimpleAnnotationReader $annotationReader) : Compon
}

/**
* Proxy to `SimpleAnnotationReader::getMethodAnnotation()` with validation
* Proxy to `AnnotationReader::getMethodAnnotation()` with validation
*
* @param SimpleAnnotationReader $annotationReader
* @param AnnotationReader $annotationReader
*
* @return ComponentObjectInterface
*
* @throws InvalidReferenceException
*
* @see SimpleAnnotationReader::getMethodAnnotation()
* @see AnnotationReader::getMethodAnnotation()
*/
private function getMethodAnnotation(SimpleAnnotationReader $annotationReader) : ComponentObjectInterface
private function getMethodAnnotation(AnnotationReader $annotationReader) : ComponentObjectInterface
{
if (!method_exists($this->class, $this->method)) {
$message = 'Annotation %s refers to non-existent method %s::%s()';
Expand All @@ -163,17 +163,17 @@ private function getMethodAnnotation(SimpleAnnotationReader $annotationReader) :
}

/**
* Proxy to `SimpleAnnotationReader::getPropertyAnnotation()` with validation
* Proxy to `AnnotationReader::getPropertyAnnotation()` with validation
*
* @param SimpleAnnotationReader $annotationReader
* @param AnnotationReader $annotationReader
*
* @return ComponentObjectInterface
*
* @throws InvalidReferenceException
*
* @see SimpleAnnotationReader::getPropertyAnnotation()
* @see AnnotationReader::getPropertyAnnotation()
*/
private function getPropertyAnnotation(SimpleAnnotationReader $annotationReader) : ComponentObjectInterface
private function getPropertyAnnotation(AnnotationReader $annotationReader) : ComponentObjectInterface
{
if (!property_exists($this->class, $this->property)) {
$message = 'Annotation %s refers to non-existent property %s::$%s';
Expand All @@ -198,17 +198,17 @@ private function getPropertyAnnotation(SimpleAnnotationReader $annotationReader)
}

/**
* Proxy to `SimpleAnnotationReader::getClassAnnotation()` with validation
* Proxy to `AnnotationReader::getClassAnnotation()` with validation
*
* @param SimpleAnnotationReader $annotationReader
* @param AnnotationReader $annotationReader
*
* @return ComponentObjectInterface
*
* @throws InvalidReferenceException
*
* @see SimpleAnnotationReader::getClassAnnotation()
* @see AnnotationReader::getClassAnnotation()
*/
private function getClassAnnotation(SimpleAnnotationReader $annotationReader) : ComponentObjectInterface
private function getClassAnnotation(AnnotationReader $annotationReader) : ComponentObjectInterface
{
if (!class_exists($this->class)) {
$message = 'Annotation %s refers to non-existent class %s';
Expand Down
75 changes: 29 additions & 46 deletions src/Middleware/RequestBodyValidationMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
class RequestBodyValidationMiddleware implements MiddlewareInterface
{

/**
* @var bool
*/
private $useCache = false;

/**
* Constructor of the class
*
Expand All @@ -59,6 +64,14 @@ public function __construct()
}
}

/**
* @return void
*/
public function useCache() : void
{
$this->useCache = true;
}

/**
* {@inheritDoc}
*
Expand All @@ -75,35 +88,15 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
}

/**
* Tries to determine the reflection of an object that contains the `@OpenApi\Operation()` annotation
*
* @param ServerRequestInterface $request
*
* @return null|ReflectionClass
*/
protected function fetchOperationSource(ServerRequestInterface $request) : ?ReflectionClass
{
$route = $request->getAttribute(Route::ATTR_NAME_FOR_ROUTE);

if ($route instanceof RouteInterface) {
return new ReflectionClass(
$route->getRequestHandler()
);
}

return null;
}

/**
* Tries to determine a MIME type for the request body
* Tries to determine a media type for the request body
*
* @param ServerRequestInterface $request
*
* @return string
*
* @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1
*/
protected function fetchMimeType(ServerRequestInterface $request) : string
protected function fetchMediaType(ServerRequestInterface $request) : string
{
$result = $request->getHeaderLine('Content-Type');
$semicolon = strpos($result, ';');
Expand All @@ -115,29 +108,6 @@ protected function fetchMimeType(ServerRequestInterface $request) : string
return $result;
}

/**
* Tries to determine a JSON schema for the request body
*
* @param ServerRequestInterface $request
*
* @return mixed
*/
protected function fetchJsonSchema(ServerRequestInterface $request)
{
$operationSource = $this->fetchOperationSource($request);

// it is not recommended to use this middleware globally...
if (null === $operationSource) {
return null;
}

$builder = new JsonSchemaBuilder($operationSource);

$mimeType = $this->fetchMimeType($request);

return $builder->forRequestBody($mimeType);
}

/**
* Validates the given request
*
Expand All @@ -150,8 +120,21 @@ protected function fetchJsonSchema(ServerRequestInterface $request)
*/
protected function validate(ServerRequestInterface $request) : void
{
$route = $request->getAttribute(Route::ATTR_NAME_FOR_ROUTE);
if (!($route instanceof RouteInterface)) {
return;
}

$operationSource = new ReflectionClass($route->getRequestHandler());
$jsonSchemaBuilder = new JsonSchemaBuilder($operationSource);

if ($this->useCache) {
$jsonSchemaBuilder->useCache();
}

try {
$jsonSchema = $this->fetchJsonSchema($request);
$mediaType = $this->fetchMediaType($request);
$jsonSchema = $jsonSchemaBuilder->forRequestBody($mediaType);
} catch (LocalUnsupportedMediaTypeException $e) {
throw new UnsupportedMediaTypeException($e->getMessage(), [
'type' => $e->getType(),
Expand Down
20 changes: 18 additions & 2 deletions src/Middleware/RequestQueryValidationMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
class RequestQueryValidationMiddleware implements MiddlewareInterface
{

/**
* @var bool
*/
private $useCache = false;

/**
* Constructor of the class
*
Expand All @@ -55,6 +60,14 @@ public function __construct()
}
}

/**
* @return void
*/
public function useCache() : void
{
$this->useCache = true;
}

/**
* {@inheritDoc}
*
Expand Down Expand Up @@ -82,15 +95,18 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
protected function validate(ServerRequestInterface $request) : void
{
$route = $request->getAttribute(Route::ATTR_NAME_FOR_ROUTE);

if (!($route instanceof RouteInterface)) {
return;
}

$operationSource = new ReflectionClass($route->getRequestHandler());
$jsonSchemaBuilder = new JsonSchemaBuilder($operationSource);
$jsonSchema = $jsonSchemaBuilder->forRequestQueryParams();

if ($this->useCache) {
$jsonSchemaBuilder->useCache();
}

$jsonSchema = $jsonSchemaBuilder->forRequestQueryParams();
if (null === $jsonSchema) {
return;
}
Expand Down
48 changes: 47 additions & 1 deletion src/Utility/JsonSchemaBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,26 @@
/**
* Import classes
*/
use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Annotations\Reader as AnnotationReader;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Cache\ApcuCache;
use Sunrise\Http\Router\OpenApi\Annotation\OpenApi\Operation;
use Sunrise\Http\Router\OpenApi\Annotation\OpenApi\ParameterReference;
use Sunrise\Http\Router\OpenApi\Annotation\OpenApi\RequestBodyReference;
use Sunrise\Http\Router\OpenApi\Annotation\OpenApi\ResponseReference;
use Sunrise\Http\Router\OpenApi\Exception\UnsupportedMediaTypeException;
use Sunrise\Http\Router\OpenApi\OpenApi;
use ReflectionClass;
use RuntimeException;

/**
* Import functions
*/
use function array_keys;
use function array_walk;
use function array_walk_recursive;
use function extension_loaded;
use function str_replace;

/**
Expand All @@ -50,10 +55,15 @@ class JsonSchemaBuilder
private $operationSource;

/**
* @var SimpleAnnotationReader
* @var AnnotationReader
*/
private $annotationReader;

/**
* @var bool
*/
private $useCache = false;

/**
* Constructor of the class
*
Expand All @@ -67,6 +77,42 @@ public function __construct(ReflectionClass $operationSource)
$this->annotationReader->addNamespace(OpenApi::ANNOTATIONS_NAMESPACE);
}

/**
* @return ReflectionClass
*/
public function getOperationSource() : ReflectionClass
{
return $this->operationSource;
}

/**
* @return AnnotationReader
*/
public function getAnnotationReader() : AnnotationReader
{
return $this->annotationReader;
}

/**
* @return void
*
* @throws RuntimeException
*/
public function useCache() : void
{
if ($this->useCache) {
throw new RuntimeException('Cache already used.');
}

if (!extension_loaded('apcu')) {
throw new RuntimeException('APCu extension required.');
}

$this->useCache = true;

$this->annotationReader = new CachedReader($this->annotationReader, new ApcuCache(__CLASS__), false);
}

/**
* Builds a JSON schema for a request query parameters
*
Expand Down
Loading

0 comments on commit cb64b9d

Please sign in to comment.