diff --git a/README.md b/README.md index d4fd303a..fd87bc56 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ A good API is one of most effective ways to improve the experience for your clients. Standardized approaches for data formats and communication protocols increase productivity and make integration between applications smooth. -This framework agnostic package fully implements [JSON API](http://jsonapi.org/) specification and helps you to focus on core application functionality rather than on protocol implementation. It supports document structure, errors and data fetching as described in [JSON API Format](http://jsonapi.org/format/). As it is designed to stay framework agnostic for practical usage it requires framework integration. [Limoncello](https://github.com/neomerx/limoncello) is an example of integration with Symfony based projects. +This framework agnostic package fully implements [JSON API](http://jsonapi.org/) specification and helps focusing on core application functionality rather than on protocol implementation. It supports document structure, errors and data fetching as described in [JSON API Format](http://jsonapi.org/format/). As it is designed to stay framework agnostic for practical usage it requires framework integration. [Limoncello](https://github.com/neomerx/limoncello) is an example of integration with Symfony based projects. -If you are looking for quick start application consider [Limoncello collins](https://github.com/neomerx/limoncello-collins) which is a pre-configured Laravel-based quick start application. +If you are looking for quick start application consider [Limoncello Collins](https://github.com/neomerx/limoncello-collins) which is a pre-configured Laravel-based quick start application. -Encoding fully support +Encoding fully support specification. In particular * Resource attributes and complex attributes * Compound documents with included resources @@ -24,7 +24,7 @@ Encoding fully support * Pagination links * Errors -The package covers all the complexity of parsing and checking request parameters and headers. For instance it helps to correctly respond with ```Unsupported Media Type``` (HTTP code 415) and ```Not Acceptable``` (HTTP code 406) to invalid requests. You don't need to manually validate all input parameters on every request. You can configure what parameters are supported by your services and this package will check incoming requests automatically. It greatly simplifies API development. All parameters from the specification are supported +The package covers all the complexity of parsing and checking HTTP request parameters and headers. For instance it helps to correctly respond with ```Unsupported Media Type``` (HTTP code 415) and ```Not Acceptable``` (HTTP code 406) to invalid requests. You don't need to manually validate all input parameters on every request. You can configure what parameters are supported by your services and this package will check incoming requests automatically. It greatly simplifies API development. All parameters from the specification are supported * Inclusion of related resources * Sparse fields @@ -43,7 +43,7 @@ Thank you for your support :star:. Via Composer ``` -$ composer require neomerx/json-api ~0.2 +$ composer require neomerx/json-api ~0.3 ``` ## Usage @@ -294,12 +294,33 @@ class PostSchema extends SchemaProvider Apart from ```self::DATA``` the following keys are supported -* ```self::INCLUDED``` (bool) - if resource(s) from this link should be added to ```included``` section. -* ```self::SHOW_META``` (bool) - if resource meta information should be added to output link description. -* ```self::SHOW_LINKAGE``` (bool) - if linkage information should be added to output link description. -* ```self::SHOW_SELF``` (bool) - if link ```self``` URL should be added to output link description. Setting this option to ```true``` requires ```self::SELF_CONTROLLER``` (```mixed```) to be set as well. -* ```self::SHOW_RELATED``` (bool) - if link ```self``` URL should be added to output link description. Setting this option to ```true``` requires ```self::RELATED_CONTROLLER``` (```mixed```) to be set as well. -* ```self::SHOW_AS_REF``` (bool) - if this key is set to ```true``` the link will be rendered as a reference. +* ```self::SHOW_AS_REF``` (bool) - if this key is set to ```true``` the link will be rendered as a reference. Default ```false```. +* ```self::SHOW_META``` (bool) - if resource meta information should be added to output link description. Default ```false```. +* ```self::SHOW_LINKAGE``` (bool) - if linkage information should be added to output link description. Default ```true```. +* ```self::SHOW_SELF``` (bool) - if link ```self``` URL should be added to output link description. Default ```false```. +* ```self::SHOW_RELATED``` (bool) - if link ```related``` URL should be added to output link description. Default ```false```. + +### Include linked resources + +By default linked resources will not be included to ```included``` output section. However you can change this behaviour by defining 'include paths'. Nested links should be separated by a dot ('.'). For instance + +```php +class PostSchema extends SchemaProvider +{ + ... + public function getIncludePaths() + { + return [ + 'posts', + 'posts.author', + 'posts.comments', + ]; + } + ... +} +``` + +If you specify ```EncodingParametersInterface``` with 'included paths' set to ```Encoder::encode``` method it will override include paths in the ```Schema```. ### Provide resource's meta information @@ -348,19 +369,6 @@ class YourSchema extends SchemaProvider } ``` -### Limit depth of resource inclusion - -By default the inclusion depth is unlimited thus all the data you pass to encoder will be put to json. You can limit the parsing depth for resource and its links by setting ```$defaultParseDepth```. - -```php -class YourSchema extends SchemaProvider -{ - ... - protected $defaultParseDepth = 1; - ... -} -``` - ### Show top level links and meta information ```php diff --git a/sample/README.md b/sample/README.md index 0ba11daf..4ade950c 100644 --- a/sample/README.md +++ b/sample/README.md @@ -48,8 +48,8 @@ If your system has debug assertions enabled it is recommended to turn them off. |Debug asserts mode |Command |Execution time| |---------------------|--------------------------------------------------|--------------| -|Enabled |```$ php -d assert.active=1 sample.php -t=10000```|16.208s | -|Disabled |```$ php -d assert.active=0 sample.php -t=10000```|3.990s | +|Enabled |```$ php -d assert.active=1 sample.php -t=10000```|12.877s | +|Disabled |```$ php -d assert.active=0 sample.php -t=10000```|6.758s | The following command could be used for performance profiling with [blackfire.io](https://blackfire.io/) diff --git a/sample/composer.json b/sample/composer.json index 264476d6..bf7f8286 100644 --- a/sample/composer.json +++ b/sample/composer.json @@ -16,6 +16,6 @@ }, "minimum-stability": "dev", "require": { - "neomerx/json-api": "0.2.2" + "neomerx/json-api": "0.3.0" } } diff --git a/sample/schemas/CommentSchema.php b/sample/schemas/CommentSchema.php index a48334a4..a4535c3d 100644 --- a/sample/schemas/CommentSchema.php +++ b/sample/schemas/CommentSchema.php @@ -46,7 +46,7 @@ public function getLinks($comment) { /** @var Comment $comment */ return [ - 'author' => [self::DATA => $comment->author, self::INCLUDED => true], + 'author' => [self::DATA => $comment->author], ]; } } diff --git a/sample/schemas/PostSchema.php b/sample/schemas/PostSchema.php index d4cf5c42..34bbfb1c 100644 --- a/sample/schemas/PostSchema.php +++ b/sample/schemas/PostSchema.php @@ -45,8 +45,8 @@ public function getLinks($post) { /** @var Post $post */ return [ - 'author' => [self::DATA => $post->author, self::INCLUDED => true], - 'comments' => [self::DATA => $post->comments, self::INCLUDED => true], + 'author' => [self::DATA => $post->author], + 'comments' => [self::DATA => $post->comments], ]; } } diff --git a/sample/schemas/SiteSchema.php b/sample/schemas/SiteSchema.php index 4276aaa4..c84c94ea 100644 --- a/sample/schemas/SiteSchema.php +++ b/sample/schemas/SiteSchema.php @@ -44,7 +44,16 @@ public function getLinks($site) { /** @var Site $site */ return [ - 'posts' => [self::DATA => $site->posts, self::INCLUDED => true], + 'posts' => [self::DATA => $site->posts], + ]; + } + + public function getIncludePaths() + { + return [ + 'posts', + 'posts.author', + 'posts.comments', ]; } } diff --git a/src/Contracts/Encoder/Handlers/HandlerFactoryInterface.php b/src/Contracts/Encoder/Handlers/HandlerFactoryInterface.php index bd488d15..93bcfe4b 100644 --- a/src/Contracts/Encoder/Handlers/HandlerFactoryInterface.php +++ b/src/Contracts/Encoder/Handlers/HandlerFactoryInterface.php @@ -27,10 +27,10 @@ interface HandlerFactoryInterface /** * Create parser reply interpreter. * - * @param DocumentInterface $document - * @param EncodingParametersInterface|null $parameters + * @param DocumentInterface $document + * @param EncodingParametersInterface $parameters * * @return ReplyInterpreterInterface */ - public function createReplyInterpreter(DocumentInterface $document, EncodingParametersInterface $parameters = null); + public function createReplyInterpreter(DocumentInterface $document, EncodingParametersInterface $parameters); } diff --git a/src/Contracts/Encoder/Stack/StackFrameReadOnlyInterface.php b/src/Contracts/Encoder/Stack/StackFrameReadOnlyInterface.php index 0a5f099a..be03d039 100644 --- a/src/Contracts/Encoder/Stack/StackFrameReadOnlyInterface.php +++ b/src/Contracts/Encoder/Stack/StackFrameReadOnlyInterface.php @@ -51,12 +51,4 @@ public function getLevel(); * @return string|null */ public function getPath(); - - /** - * If all resources on frame path have 'should be included' flag set to true. - * For root resource (level 1) it returns null as it does not have a link object. - * - * @return bool|null - */ - public function isPathIncluded(); } diff --git a/src/Contracts/Exceptions/RenderContainerInterface.php b/src/Contracts/Exceptions/RenderContainerInterface.php index 658fc5fa..c8ebd84f 100644 --- a/src/Contracts/Exceptions/RenderContainerInterface.php +++ b/src/Contracts/Exceptions/RenderContainerInterface.php @@ -41,7 +41,16 @@ public function registerRender($exceptionClass, Closure $render); * * @return void */ - public function registerMapping(array $exceptionMapping); + public function registerHttpCodeMapping(array $exceptionMapping); + + /** + * Register JSON API Error object renders mapping for exceptions. + * + * @param array $exceptionMapping + * + * @return void + */ + public function registerJsonApiErrorMapping(array $exceptionMapping); /** * Get registered or default render for exception. diff --git a/src/Contracts/Schema/LinkObjectInterface.php b/src/Contracts/Schema/LinkObjectInterface.php index 8f728602..59575003 100644 --- a/src/Contracts/Schema/LinkObjectInterface.php +++ b/src/Contracts/Schema/LinkObjectInterface.php @@ -70,13 +70,6 @@ public function isShowLinkage(); */ public function getRelatedSubUrl(); - /** - * If link object should be included. - * - * @return bool - */ - public function isShouldBeIncluded(); - /** * If 'meta' should be shown. * @@ -98,20 +91,6 @@ public function isShowPagination(); */ public function getLinkedData(); - /** - * Get 'self' controller data. - * - * @return mixed - */ - public function getSelfControllerData(); - - /** - * Get 'related' controller data. - * - * @return mixed - */ - public function getRelatedControllerData(); - /** * Get pagination information. * diff --git a/src/Contracts/Schema/ResourceObjectInterface.php b/src/Contracts/Schema/ResourceObjectInterface.php index fca1a974..e9006cf7 100644 --- a/src/Contracts/Schema/ResourceObjectInterface.php +++ b/src/Contracts/Schema/ResourceObjectInterface.php @@ -56,13 +56,6 @@ public function getMeta(); */ public function getSelfUrl(); - /** - * Get 'self' controller data. - * - * @return mixed - */ - public function getSelfControllerData(); - /** * If 'self' endpoint URL should be shown for resource in 'data' section. * @@ -91,6 +84,13 @@ public function isShowSelfInIncluded(); */ public function isShowMetaInIncluded(); + /** + * If 'meta' should be shown in linkages. + * + * @return bool + */ + public function isShowMetaInLinkage(); + /** * If resources links should be shown for included resources. * diff --git a/src/Contracts/Schema/SchemaFactoryInterface.php b/src/Contracts/Schema/SchemaFactoryInterface.php index 48765691..27861fa0 100644 --- a/src/Contracts/Schema/SchemaFactoryInterface.php +++ b/src/Contracts/Schema/SchemaFactoryInterface.php @@ -39,12 +39,12 @@ public function createContainer(array $providers = []); * @param array $attributes * @param mixed $meta * @param string $selfUrl - * @param mixed $selfControllerData * @param bool $isShowSelf * @param bool $isShowMeta * @param bool $isShowSelfInIncluded * @param bool $isShowLinksInIncluded * @param bool $isShowMetaInIncluded + * @param bool $isShowMetaInLinkage * * @return ResourceObjectInterface */ @@ -55,12 +55,12 @@ public function createResourceObject( array $attributes, $meta, $selfUrl, - $selfControllerData, $isShowSelf, $isShowMeta, $isShowSelfInIncluded, $isShowLinksInIncluded, - $isShowMetaInIncluded + $isShowMetaInIncluded, + $isShowMetaInLinkage ); /** @@ -76,9 +76,6 @@ public function createResourceObject( * @param bool $isShowLinkage * @param bool $isShowMeta * @param bool $isShowPagination - * @param bool $isIncluded - * @param mixed $selfControllerData - * @param mixed $relatedControllerData * @param PaginationLinksInterface|null $pagination * * @return LinkObjectInterface @@ -94,9 +91,6 @@ public function createLinkObject( $isShowLinkage, $isShowMeta, $isShowPagination, - $isIncluded, - $selfControllerData, - $relatedControllerData, $pagination ); diff --git a/src/Contracts/Schema/SchemaProviderInterface.php b/src/Contracts/Schema/SchemaProviderInterface.php index ce2ce6aa..49302393 100644 --- a/src/Contracts/Schema/SchemaProviderInterface.php +++ b/src/Contracts/Schema/SchemaProviderInterface.php @@ -75,6 +75,20 @@ public function getLinkObjectIterator($resource); */ public function getMeta($resource); + /** + * If 'self' endpoint URL. + * + * @return bool + */ + public function isShowSelf(); + + /** + * If 'meta' should be shown for resource. + * + * @return bool + */ + public function isShowMeta(); + /** * If 'self' endpoint URL should be shown for included resources. * @@ -97,12 +111,11 @@ public function isShowLinksInIncluded(); public function isShowMetaInIncluded(); /** - * Get default depth for object inclusion to 'include' section. - * If any other setting is not available this value will be used as a limiter. + * If 'meta' should be shown in linkages. * - * @return int + * @return bool */ - public function getDefaultParseDepth(); + public function isShowMetaInLinkage(); /** * Create resource object. @@ -114,4 +127,11 @@ public function getDefaultParseDepth(); * @return ResourceObjectInterface */ public function createResourceObject($resource, $isOriginallyArrayed, array $attributeKeysFilter = null); + + /** + * Get schema default include paths. + * + * @return string[] + */ + public function getIncludePaths(); } diff --git a/src/Document/Presenters/ElementPresenter.php b/src/Document/Presenters/ElementPresenter.php index 83ac3b46..32669ad4 100644 --- a/src/Document/Presenters/ElementPresenter.php +++ b/src/Document/Presenters/ElementPresenter.php @@ -180,10 +180,14 @@ public function concatUrls($url, $subUrl) */ private function getLinkageRepresentation(ResourceObjectInterface $resource) { - return [ + $representation = [ Document::KEYWORD_TYPE => $resource->getType(), Document::KEYWORD_ID => $resource->getId(), ]; + if ($resource->isShowMetaInLinkage() === true) { + $representation[Document::KEYWORD_META] = $resource->getMeta(); + } + return $representation; } /** diff --git a/src/Encoder/Encoder.php b/src/Encoder/Encoder.php index 63a52b7b..0ecffb57 100644 --- a/src/Encoder/Encoder.php +++ b/src/Encoder/Encoder.php @@ -18,6 +18,7 @@ use \Neomerx\JsonApi\Contracts\Document\ErrorInterface; use \Neomerx\JsonApi\Contracts\Encoder\EncoderInterface; +use Neomerx\JsonApi\Contracts\Parameters\ParametersFactoryInterface; use \Neomerx\JsonApi\Contracts\Schema\ContainerInterface; use \Neomerx\JsonApi\Contracts\Document\DocumentLinksInterface; use \Neomerx\JsonApi\Contracts\Document\DocumentFactoryInterface; @@ -50,30 +51,38 @@ class Encoder implements EncoderInterface */ private $handlerFactory; + /** + * @var ParametersFactoryInterface + */ + private $parametersFactory; + /** * @var JsonEncodeOptions|null */ protected $encodeOptions; /** - * @param DocumentFactoryInterface $documentFactory - * @param ParserFactoryInterface $parserFactory - * @param HandlerFactoryInterface $handlerFactory - * @param ContainerInterface $container - * @param JsonEncodeOptions|null $encodeOptions + * @param DocumentFactoryInterface $documentFactory + * @param ParserFactoryInterface $parserFactory + * @param HandlerFactoryInterface $handlerFactory + * @param ParametersFactoryInterface $parametersFactory + * @param ContainerInterface $container + * @param JsonEncodeOptions|null $encodeOptions */ public function __construct( DocumentFactoryInterface $documentFactory, ParserFactoryInterface $parserFactory, HandlerFactoryInterface $handlerFactory, + ParametersFactoryInterface $parametersFactory, ContainerInterface $container, JsonEncodeOptions $encodeOptions = null ) { - $this->container = $container; - $this->encodeOptions = $encodeOptions; - $this->parserFactory = $parserFactory; - $this->handlerFactory = $handlerFactory; - $this->documentFactory = $documentFactory; + $this->container = $container; + $this->encodeOptions = $encodeOptions; + $this->parserFactory = $parserFactory; + $this->handlerFactory = $handlerFactory; + $this->documentFactory = $documentFactory; + $this->parametersFactory = $parametersFactory; } /** @@ -86,7 +95,8 @@ public function encode( EncodingParametersInterface $parameters = null ) { $docWriter = $this->documentFactory->createDocument(); - $parserManager = $parameters !== null ? $this->parserFactory->createManager($parameters) : null; + $parameters = $this->getEncodingParameters($data, $parameters); + $parserManager = $this->parserFactory->createManager($parameters); $parser = $this->parserFactory->createParser($this->container, $parserManager); $interpreter = $this->handlerFactory->createReplyInterpreter($docWriter, $parameters); foreach ($parser->parse($data) as $reply) { @@ -156,7 +166,37 @@ public static function instance(array $schemas, JsonEncodeOptions $encodeOptions $documentFactory = new \Neomerx\JsonApi\Document\DocumentFactory(); /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */ $encoderFactory = new \Neomerx\JsonApi\Encoder\Factory\EncoderFactory(); + /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */ + $parameterFactory = new \Neomerx\JsonApi\Parameters\ParametersFactory(); + + return new self( + $documentFactory, + $encoderFactory, + $encoderFactory, + $parameterFactory, + $container, + $encodeOptions + ); + } - return new self($documentFactory, $encoderFactory, $encoderFactory, $container, $encodeOptions); + /** + * @param array|object|null $data + * @param EncodingParametersInterface|null $parameters + * + * @return EncodingParametersInterface + */ + private function getEncodingParameters($data, EncodingParametersInterface $parameters = null) + { + if (empty($data) === true && $parameters === null) { + return $this->parametersFactory->createEncodingParameters(); + } elseif ($parameters !== null && $parameters->getIncludePaths() !== null) { + return $parameters; + } else { + $schema = $this->container->getSchema(is_array($data) ? $data[0] : $data); + $includePaths = $schema->getIncludePaths(); + $fieldSets = $parameters === null ? null : $parameters->getFieldSets(); + + return $this->parametersFactory->createEncodingParameters($includePaths, $fieldSets); + } } } diff --git a/src/Encoder/Factory/EncoderFactory.php b/src/Encoder/Factory/EncoderFactory.php index c53605d5..a51c6407 100644 --- a/src/Encoder/Factory/EncoderFactory.php +++ b/src/Encoder/Factory/EncoderFactory.php @@ -91,7 +91,7 @@ public function createStack() /** * @inheritdoc */ - public function createReplyInterpreter(DocumentInterface $document, EncodingParametersInterface $parameters = null) + public function createReplyInterpreter(DocumentInterface $document, EncodingParametersInterface $parameters) { return new ReplyInterpreter($document, $parameters); } diff --git a/src/Encoder/Handlers/ReplyInterpreter.php b/src/Encoder/Handlers/ReplyInterpreter.php index b9f98a75..d80951fc 100644 --- a/src/Encoder/Handlers/ReplyInterpreter.php +++ b/src/Encoder/Handlers/ReplyInterpreter.php @@ -38,10 +38,10 @@ class ReplyInterpreter implements ReplyInterpreterInterface private $parameters; /** - * @param DocumentInterface $document - * @param EncodingParametersInterface|null $parameters + * @param DocumentInterface $document + * @param EncodingParametersInterface $parameters */ - public function __construct(DocumentInterface $document, EncodingParametersInterface $parameters = null) + public function __construct(DocumentInterface $document, EncodingParametersInterface $parameters) { $this->document = $document; $this->parameters = $parameters; @@ -81,16 +81,11 @@ public function handle(ParserReplyInterface $reply) */ protected function handleLinks(ParserReplyInterface $reply, Frame $current, Frame $previous) { - assert('$previous !== null'); + $this->addToIncludedAndCheckIfParentIsTarget($reply, $current, $previous); + if ($this->isLinkInFieldSet($current, $previous) === true) { $this->addLinkToData($reply, $current, $previous); } - if ($current->isPathIncluded() === true) { - list(, $currentIsTarget) = $this->getIfTargets($current, $previous, $this->parameters); - if ($currentIsTarget === true) { - $this->addToIncluded($reply, $current); - } - } } /** @@ -100,17 +95,29 @@ protected function handleLinks(ParserReplyInterface $reply, Frame $current, Fram */ protected function handleIncluded(ParserReplyInterface $reply, Frame $current, Frame $previous) { - list($parentIsTarget, $currentIsTarget) = $this->getIfTargets($current, $previous, $this->parameters); - - if ($previous->isPathIncluded() === true && $parentIsTarget === true && + if ($this->addToIncludedAndCheckIfParentIsTarget($reply, $current, $previous) === true && $this->isLinkInFieldSet($current, $previous) === true ) { $this->addLinkToIncluded($reply, $current, $previous); } + } - if ($current->isPathIncluded() === true && $currentIsTarget === true) { + /** + * @param ParserReplyInterface $reply + * @param Frame $current + * @param Frame $previous + * + * @return bool + */ + private function addToIncludedAndCheckIfParentIsTarget(ParserReplyInterface $reply, Frame $current, Frame $previous) + { + list($parentIsTarget, $currentIsTarget) = $this->getIfTargets($current, $previous); + + if ($currentIsTarget === true) { $this->addToIncluded($reply, $current); } + + return $parentIsTarget; } /** @@ -218,21 +225,15 @@ private function setResourceCompleted(Frame $current) } /** - * @param Frame $current - * @param Frame|null $previous - * @param EncodingParametersInterface|null $parameters + * @param Frame $current + * @param Frame|null $previous * * @return bool[] */ private function getIfTargets( Frame $current, - Frame $previous = null, - EncodingParametersInterface $parameters = null + Frame $previous = null ) { - if ($parameters === null) { - return [true, true]; - } - $parentIsTarget = ($previous === null || $this->parameters->isPathIncluded($previous->getPath())); $currentIsTarget = $this->parameters->isPathIncluded($current->getPath()); @@ -249,9 +250,7 @@ private function getIfTargets( */ private function isLinkInFieldSet(Frame $current, Frame $previous) { - if ($this->parameters === null || - ($fieldSet = $this->parameters->getFieldSet($previous->getResourceObject()->getType())) === null - ) { + if (($fieldSet = $this->parameters->getFieldSet($previous->getResourceObject()->getType())) === null) { return true; } diff --git a/src/Encoder/Parser/Parser.php b/src/Encoder/Parser/Parser.php index 7056d424..31263f6a 100644 --- a/src/Encoder/Parser/Parser.php +++ b/src/Encoder/Parser/Parser.php @@ -82,11 +82,6 @@ class Parser implements ParserInterface */ private $manager; - /** - * @var int - */ - private $maxLevel; - /** * @param ParserFactoryInterface $parserFactory * @param StackFactoryInterface $stackFactory @@ -145,10 +140,6 @@ private function parseData($data) $data = [$data]; } - if ($curFrame->getLevel() === 1) { - $this->maxLevel = $schema->getDefaultParseDepth(); - } - // duplicated are allowed in data however they shouldn't be in includes $isDupAllowed = $curFrame->getLevel() < 2; @@ -161,7 +152,6 @@ private function parseData($data) yield $this->createReplyResourceStarted(); if (($isCircular === true && $isDupAllowed === false) || - $this->maxLevel !== null && $curFrame->getLevel() > $this->maxLevel || $this->shouldParseLinks($resourceObject, $isCircular) === false ) { continue; diff --git a/src/Encoder/Parser/ParserManager.php b/src/Encoder/Parser/ParserManager.php index d9fc263d..9c697dc9 100644 --- a/src/Encoder/Parser/ParserManager.php +++ b/src/Encoder/Parser/ParserManager.php @@ -68,21 +68,22 @@ private function foundInPaths(StackReadOnlyInterface $stack) if ($stack->count() < 2) { // top level, no resources ware started to parse yet return [true, false]; - } elseif (($includePaths = $this->parameters->getIncludePaths()) === null) { - // not include path filter => all resources should be considered as targets - return [true, true]; } + $onTheWay = true; $parentIsTarget = $this->parameters->isPathIncluded($stack->penult()->getPath()); - $onTheWay = false; - $path = $stack->end()->getPath(); - foreach ($includePaths as $targetPath) { - if (strpos($targetPath, $path) === 0) { - $onTheWay = true; - break; + if (($includePaths = $this->parameters->getIncludePaths()) !== null) { + $onTheWay = false; + $path = $stack->end()->getPath(); + foreach ($includePaths as $targetPath) { + if (strpos($targetPath, $path) === 0) { + $onTheWay = true; + break; + } } } + return [$onTheWay, $parentIsTarget]; } } diff --git a/src/Encoder/Stack/StackFrame.php b/src/Encoder/Stack/StackFrame.php index f5da103c..1ab98709 100644 --- a/src/Encoder/Stack/StackFrame.php +++ b/src/Encoder/Stack/StackFrame.php @@ -51,11 +51,6 @@ class StackFrame implements StackFrameInterface */ private $path = null; - /** - * @var bool|null - */ - private $isPathIncluded = null; - /** * @param int $level * @param StackFrameReadOnlyInterface|null $previous @@ -98,7 +93,6 @@ public function setLinkObject(LinkObjectInterface $linkObject) $this->linkObject = $linkObject; $this->setCurrentPath(); - $this->setIsPathToCurrentIsIncluded(); } /** @@ -125,14 +119,6 @@ public function getPath() return $this->path; } - /** - * @inheritdoc - */ - public function isPathIncluded() - { - return $this->isPathIncluded; - } - /** * Set path to current frame. */ @@ -144,19 +130,4 @@ private function setCurrentPath() $this->path = $this->previous->getPath() . '.' . $this->linkObject->getName(); } } - - /** - * Determine if all elements on the path to current frame should be included. - */ - private function setIsPathToCurrentIsIncluded() - { - assert('$this->level > 1'); - if ($this->level === 2) { - $this->isPathIncluded = $this->linkObject->isShouldBeIncluded(); - } elseif ($this->level > 2) { - $isPreviousIncluded = $this->previous->isPathIncluded(); - assert('is_bool($isPreviousIncluded)'); - $this->isPathIncluded = $isPreviousIncluded && $this->linkObject->isShouldBeIncluded(); - } - } } diff --git a/src/Exceptions/RenderContainer.php b/src/Exceptions/RenderContainer.php index 71df26dd..0893c2a1 100644 --- a/src/Exceptions/RenderContainer.php +++ b/src/Exceptions/RenderContainer.php @@ -18,7 +18,16 @@ use \Closure; use \Exception; +use \Neomerx\JsonApi\Encoder\Encoder; +use Neomerx\JsonApi\Encoder\JsonEncodeOptions; +use \Neomerx\JsonApi\Responses\Responses; +use \Neomerx\JsonApi\Parameters\MediaType; +use \Neomerx\JsonApi\Contracts\Document\ErrorInterface; +use \Neomerx\JsonApi\Contracts\Responses\ResponsesInterface; +use \Neomerx\JsonApi\Contracts\Codec\CodecContainerInterface; use \Neomerx\JsonApi\Contracts\Exceptions\RenderContainerInterface; +use \Neomerx\JsonApi\Contracts\Integration\NativeResponsesInterface; +use \Neomerx\JsonApi\Contracts\Parameters\SupportedExtensionsInterface; /** * @package Neomerx\JsonApi @@ -30,10 +39,15 @@ class RenderContainer implements RenderContainerInterface */ private $renders = []; + /** + * @var ResponsesInterface + */ + private $responses; + /** * @var Closure */ - private $codeResponseClosure; + private $extensionsClosure; /** * @var int @@ -41,15 +55,17 @@ class RenderContainer implements RenderContainerInterface private $defaultStatusCode; /** - * @param Closure $codeResponse Closure accept $statusCode and returns Response. - * @param int $defaultStatusCode Default status code for unknown exceptions. + * @param NativeResponsesInterface $responses + * @param Closure $extensionsClosure Closure returns extensions for the current request/controller. + * @param int $defaultStatusCode Default status code for unknown exceptions. */ - public function __construct(Closure $codeResponse, $defaultStatusCode) + public function __construct(NativeResponsesInterface $responses, Closure $extensionsClosure, $defaultStatusCode) { assert('is_int($defaultStatusCode) && $defaultStatusCode >= 500 && $defaultStatusCode < 600'); - $this->codeResponseClosure = $codeResponse; - $this->defaultStatusCode = $defaultStatusCode; + $this->responses = new Responses($responses); + $this->extensionsClosure = $extensionsClosure; + $this->defaultStatusCode = $defaultStatusCode; } /** @@ -61,13 +77,9 @@ public function registerRender($exceptionClass, Closure $render) } /** - * Register HTTP status code mapping for exceptions. - * - * @param array $exceptionMapping - * - * @return void + * @inheritdoc */ - public function registerMapping(array $exceptionMapping) + public function registerHttpCodeMapping(array $exceptionMapping) { foreach ($exceptionMapping as $exceptionClass => $httpStatusCode) { $this->registerRender($exceptionClass, $this->getHttpCodeRender($httpStatusCode)); @@ -75,11 +87,17 @@ public function registerMapping(array $exceptionMapping) } /** - * Get registered or default render for exception. - * - * @param Exception $exception - * - * @return Closure + * @inheritdoc + */ + public function registerJsonApiErrorMapping(array $exceptionMapping) + { + foreach ($exceptionMapping as $exceptionClass => $httpStatusCode) { + $this->registerRender($exceptionClass, $this->getErrorsRender($httpStatusCode)); + } + } + + /** + * @inheritdoc */ public function getRender(Exception $exception) { @@ -111,8 +129,41 @@ protected function getDefaultRender() protected function getHttpCodeRender($statusCode) { return function () use ($statusCode) { - $codeResponseClosure = $this->codeResponseClosure; - return $codeResponseClosure($statusCode); + $extensionsClosure = $this->extensionsClosure; + /** @var SupportedExtensionsInterface $supportedExtensions */ + $supportedExtensions = $extensionsClosure(); + + $content = null; + $mediaType = new MediaType(CodecContainerInterface::JSON_API_TYPE); + + return $this->responses->getResponse($statusCode, $mediaType, $content, $supportedExtensions); + }; + } + + /** + * Get render that returns JSON API response with JSON API Error objects and specified HTTP status code. + * + * @param int $statusCode + * + * @return Closure + */ + protected function getErrorsRender($statusCode) + { + /** + * @param ErrorInterface[] $errors + * @param JsonEncodeOptions $encodeOptions + * + * @return mixed + */ + return function (array $errors, JsonEncodeOptions $encodeOptions = null) use ($statusCode) { + $extensionsClosure = $this->extensionsClosure; + /** @var SupportedExtensionsInterface $supportedExtensions */ + $supportedExtensions = $extensionsClosure(); + + $content = Encoder::instance([], $encodeOptions)->errors($errors); + $mediaType = new MediaType(CodecContainerInterface::JSON_API_TYPE); + + return $this->responses->getResponse($statusCode, $mediaType, $content, $supportedExtensions); }; } } diff --git a/src/Schema/LinkObject.php b/src/Schema/LinkObject.php index d3de8f09..2203ec01 100644 --- a/src/Schema/LinkObject.php +++ b/src/Schema/LinkObject.php @@ -54,11 +54,6 @@ class LinkObject implements LinkObjectInterface */ private $isShowLinkage; - /** - * @var bool - */ - private $isShouldBeIncluded; - /** * @var string|null */ @@ -79,16 +74,6 @@ class LinkObject implements LinkObjectInterface */ private $isShowPagination; - /** - * @var mixed - */ - private $selfControllerData; - - /** - * @var mixed - */ - private $relatedControllerData; - /** * @var PaginationLinksInterface|null */ @@ -105,9 +90,6 @@ class LinkObject implements LinkObjectInterface * @param bool $isShowLinkage * @param bool $isShowMeta * @param bool $isShowPagination - * @param bool $isIncluded - * @param mixed $selfControllerData - * @param mixed $relatedControllerData * @param PaginationLinksInterface|null $pagination */ public function __construct( @@ -121,9 +103,6 @@ public function __construct( $isShowLinkage, $isShowMeta, $isShowPagination, - $isIncluded, - $selfControllerData, - $relatedControllerData, $pagination ) { assert( @@ -132,7 +111,7 @@ public function __construct( '(is_null($selfSubUrl) || is_string($selfSubUrl)) &&'. '(is_null($relatedSubUrl) || is_string($relatedSubUrl)) &&'. 'is_bool($isShowAsRef) && is_bool($isShowSelf) && is_bool($isShowRelated) && is_bool($isShowMeta) &&'. - 'is_bool($isIncluded) && is_bool($isShowPagination) &&'. + 'is_bool($isShowPagination) &&'. '(is_null($pagination) || $pagination instanceof ' . PaginationLinksInterface::class . ')' ); assert( @@ -149,9 +128,6 @@ public function __construct( $this->isShowRelated = $isShowRelated; $this->isShowLinkage = $isShowLinkage; $this->isShowMeta = $isShowMeta; - $this->isShouldBeIncluded = $isIncluded; - $this->selfControllerData = $selfControllerData; - $this->relatedControllerData = $relatedControllerData; $this->isShowPagination = $isShowPagination; $this->pagination = $pagination; } @@ -212,14 +188,6 @@ public function getRelatedSubUrl() return $this->relatedSubUrl; } - /** - * @inheritdoc - */ - public function isShouldBeIncluded() - { - return $this->isShouldBeIncluded; - } - /** * @inheritdoc */ @@ -244,22 +212,6 @@ public function getLinkedData() return $this->data; } - /** - * @inheritdoc - */ - public function getSelfControllerData() - { - return $this->selfControllerData; - } - - /** - * @inheritdoc - */ - public function getRelatedControllerData() - { - return $this->relatedControllerData; - } - /** * @inheritdoc */ diff --git a/src/Schema/ResourceObject.php b/src/Schema/ResourceObject.php index f0ed3706..e0927314 100644 --- a/src/Schema/ResourceObject.php +++ b/src/Schema/ResourceObject.php @@ -58,11 +58,6 @@ class ResourceObject implements ResourceObjectInterface */ private $isShowMeta; - /** - * @var mixed - */ - private $selfControllerData; - /** * @var bool */ @@ -78,6 +73,11 @@ class ResourceObject implements ResourceObjectInterface */ private $isShowMetaInIncluded; + /** + * @var bool + */ + private $isShowMetaInLinkage; + /** * @var bool */ @@ -90,12 +90,12 @@ class ResourceObject implements ResourceObjectInterface * @param array $attributes * @param mixed $meta * @param string $selfUrl - * @param mixed $selfControllerData * @param bool $isShowSelf * @param bool $isShowMeta * @param bool $isShowSelfInIncluded * @param bool $isShowLinksInIncluded * @param bool $isShowMetaInIncluded + * @param bool $isShowMetaInLinkage */ public function __construct( $isInArray, @@ -104,16 +104,16 @@ public function __construct( array $attributes, $meta, $selfUrl, - $selfControllerData, $isShowSelf, $isShowMeta, $isShowSelfInIncluded, $isShowLinksInIncluded, - $isShowMetaInIncluded + $isShowMetaInIncluded, + $isShowMetaInLinkage ) { assert( 'is_bool($isInArray) && is_string($type) && is_string($idx) && is_array($attributes) &&'. - 'is_string($selfUrl) && is_bool($isShowSelf) && is_bool($isShowMeta) &&'. + 'is_string($selfUrl) && is_bool($isShowSelf) && is_bool($isShowMeta) && is_bool($isShowMetaInLinkage) &&'. 'is_bool($isShowSelfInIncluded) && is_bool($isShowLinksInIncluded) && is_bool($isShowMetaInIncluded)' ); @@ -125,10 +125,10 @@ public function __construct( $this->isShowSelf = $isShowSelf; $this->selfUrl = $selfUrl; $this->isShowMeta = $isShowMeta; - $this->selfControllerData = $selfControllerData; $this->isShowSelfInIncluded = $isShowSelfInIncluded; $this->isShowLinksInIncluded = $isShowLinksInIncluded; $this->isShowMetaInIncluded = $isShowMetaInIncluded; + $this->isShowMetaInLinkage = $isShowMetaInLinkage; } /** @@ -171,14 +171,6 @@ public function getSelfUrl() return $this->selfUrl; } - /** - * @inheritdoc - */ - public function getSelfControllerData() - { - return $this->selfControllerData; - } - /** * @inheritdoc */ @@ -211,6 +203,14 @@ public function isShowMetaInIncluded() return $this->isShowMetaInIncluded; } + /** + * @inheritdoc + */ + public function isShowMetaInLinkage() + { + return $this->isShowMetaInLinkage; + } + /** * @inheritdoc */ diff --git a/src/Schema/SchemaFactory.php b/src/Schema/SchemaFactory.php index f092df20..0f37efc7 100644 --- a/src/Schema/SchemaFactory.php +++ b/src/Schema/SchemaFactory.php @@ -41,12 +41,12 @@ public function createResourceObject( array $attributes, $meta, $selfUrl, - $selfControllerData, $isShowSelf, $isShowMeta, $isShowSelfInIncluded, $isShowLinksInIncluded, - $isShowMetaInIncluded + $isShowMetaInIncluded, + $isShowMetaInLinkage ) { return new ResourceObject( $isInArray, @@ -55,12 +55,12 @@ public function createResourceObject( $attributes, $meta, $selfUrl, - $selfControllerData, $isShowSelf, $isShowMeta, $isShowSelfInIncluded, $isShowLinksInIncluded, - $isShowMetaInIncluded + $isShowMetaInIncluded, + $isShowMetaInLinkage ); } @@ -78,9 +78,6 @@ public function createLinkObject( $isShowLinkage, $isShowMeta, $isShowPagination, - $isIncluded, - $selfControllerData, - $relatedControllerData, $pagination ) { return new LinkObject( @@ -94,9 +91,6 @@ public function createLinkObject( $isShowLinkage, $isShowMeta, $isShowPagination, - $isIncluded, - $selfControllerData, - $relatedControllerData, $pagination ); } diff --git a/src/Schema/SchemaProvider.php b/src/Schema/SchemaProvider.php index 24c4a52b..dfd3f311 100644 --- a/src/Schema/SchemaProvider.php +++ b/src/Schema/SchemaProvider.php @@ -32,9 +32,6 @@ abstract class SchemaProvider implements SchemaProviderInterface /** If link should be shown as reference. */ const SHOW_AS_REF = 'asRef'; - /** If link objects by default should be included to response. */ - const INCLUDED = 'included'; - /** If meta information should be shown. */ const SHOW_META = 'showMeta'; @@ -50,12 +47,6 @@ abstract class SchemaProvider implements SchemaProviderInterface /** If link pagination information should be shown. */ const SHOW_PAGINATION = 'showPagination'; - /** Arbitrary data describing 'self' controller. */ - const SELF_CONTROLLER = 'selfController'; - - /** Arbitrary data describing 'related' controller. */ - const RELATED_CONTROLLER = 'relatedController'; - /** Link pagination information */ const PAGINATION = 'pagination'; @@ -100,22 +91,22 @@ abstract class SchemaProvider implements SchemaProviderInterface /** * @var bool */ - protected $isShowSelfInIncluded = false; + protected $isShowMetaInLinkage = false; /** * @var bool */ - protected $isShowLinksInIncluded = false; + protected $isShowSelfInIncluded = false; /** * @var bool */ - protected $isShowMetaInIncluded = false; + protected $isShowLinksInIncluded = false; /** - * @var int + * @var bool */ - protected $defaultParseDepth = null; + protected $isShowMetaInIncluded = false; /** * @var SchemaFactoryInterface @@ -134,12 +125,6 @@ abstract class SchemaProvider implements SchemaProviderInterface public function __construct(SchemaFactoryInterface $factory, ContainerInterface $container) { assert('is_string($this->resourceType) && empty($this->resourceType) === false', 'Resource type not set.'); - assert( - 'is_null($this->defaultParseDepth) || '. - '(is_int($this->defaultParseDepth) && $this->defaultParseDepth > 0)', - 'If set depth should be positive int.' - ); - assert( 'is_bool($this->isShowSelfInIncluded) &&'. 'is_bool($this->isShowLinksInIncluded) &&'. @@ -169,25 +154,27 @@ public function getSelfUrl($resource) } /** - * Get the base self URL - * - * @return string + * @inheritdoc */ - protected function getBaseSelfUrl() + public function getMeta($resource) { - // Note: $resource is available as input parameter - - substr($this->baseSelfUrl, -1) === '/' ?: $this->baseSelfUrl .= '/'; + return null; + } - return $this->baseSelfUrl; + /** + * @inheritdoc + */ + public function isShowSelf() + { + return $this->isShowSelf; } /** * @inheritdoc */ - public function getMeta($resource) + public function isShowMeta() { - return null; + return $this->isShowMeta; } /** @@ -217,13 +204,17 @@ public function isShowMetaInIncluded() /** * @inheritdoc */ - public function getDefaultParseDepth() + public function isShowMetaInLinkage() { - return $this->defaultParseDepth; + return $this->isShowMetaInLinkage; } /** - * @inheritdoc + * Get resource links. + * + * @param object $resource + * + * @return array */ public function getLinks($resource) { @@ -237,24 +228,18 @@ public function getLinks($resource) public function getLinkObjectIterator($resource) { foreach ($this->getLinks($resource) as $name => $desc) { - $relatedController = $this->getNotEmptyValue($desc, self::RELATED_CONTROLLER); - $selfController = $this->getNotEmptyValue($desc, self::SELF_CONTROLLER); - $data = $this->readData($desc); - $isIncluded = ($this->getValue($desc, self::INCLUDED) === true); - $isShowMeta = ($this->getValue($desc, self::SHOW_META) === true); - $isShowSelf = $this->isShowControllerUrl($selfController, $desc, self::SHOW_SELF); - $isShowAsRef = $this->isShowControllerUrl($relatedController, $desc, self::SHOW_AS_REF); - $isShowRelated = $this->isShowControllerUrl($relatedController, $desc, self::SHOW_RELATED); - $isShowLinkage = ($this->getValue($desc, self::SHOW_LINKAGE, true) === true); + $data = $this->readData($desc); + $isShowMeta = ($this->getValue($desc, self::SHOW_META, false) === true); + $isShowSelf = ($this->getValue($desc, self::SHOW_SELF, false) === true); + $isShowAsRef = ($this->getValue($desc, self::SHOW_AS_REF, false) === true); + $isShowRelated = ($this->getValue($desc, self::SHOW_RELATED, false) === true); + $isShowLinkage = ($this->getValue($desc, self::SHOW_LINKAGE, true) === true); list($isShowPagination, $pagination) = $this->readPagination($desc); $selfSubUrl = $this->getValue($desc, self::SELF_SUB_URL, '/links/'.$name); $relatedSubUrl = $this->getValue($desc, self::RELATED_SUB_URL, '/'.$name); - $selfSubUrl = ($selfController === null ? null : $selfSubUrl); - $relatedSubUrl = ($relatedController === null ? null : $relatedSubUrl); - yield $this->factory->createLinkObject( $name, $data, @@ -266,9 +251,6 @@ public function getLinkObjectIterator($resource) $isShowLinkage, $isShowMeta, $isShowPagination, - $isIncluded, - $selfController, - $relatedController, $pagination ); } @@ -290,35 +272,37 @@ public function createResourceObject($resource, $isOriginallyArrayed, array $att $attributes, $this->getMeta($resource), $this->getSelfUrl($resource), - $this->getSelfControllerData(), - $this->isShowSelf, - $this->isShowMeta, + $this->isShowSelf(), + $this->isShowMeta(), $this->isShowSelfInIncluded(), $this->isShowLinksInIncluded(), - $this->isShowMetaInIncluded() + $this->isShowMetaInIncluded(), + $this->isShowMetaInLinkage() ); } /** - * Get 'self' controller data. - * - * @return mixed + * @inheritdoc */ - protected function getSelfControllerData() + public function getIncludePaths() { - return null; + return []; } /** - * @param array $array - * @param string $key - * @param mixed $default + * Get the base self URL * - * @return mixed + * @param object $resource + * + * @return string */ - private function getValue(array $array, $key, $default = null) + protected function getBaseSelfUrl($resource) { - return (isset($array[$key]) === true ? $array[$key] : $default); + $resource ?: null; + + substr($this->baseSelfUrl, -1) === '/' ?: $this->baseSelfUrl .= '/'; + + return $this->baseSelfUrl; } /** @@ -328,27 +312,9 @@ private function getValue(array $array, $key, $default = null) * * @return mixed */ - private function getNotEmptyValue(array $array, $key, $default = null) - { - if (isset($array[$key]) === true) { - $value = $array[$key]; - if (empty($value) === false) { - return $value; - } - } - return $default; - } - - /** - * @param mixed $controllerData - * @param array $description - * @param string $showKey - * - * @return bool - */ - private function isShowControllerUrl($controllerData, array $description, $showKey) + private function getValue(array $array, $key, $default = null) { - return ($controllerData !== null && $this->getValue($description, $showKey) === true); + return (isset($array[$key]) === true ? $array[$key] : $default); } /** diff --git a/tests/Data/AuthorSchema.php b/tests/Data/AuthorSchema.php index 4fd96c6b..0e176fce 100644 --- a/tests/Data/AuthorSchema.php +++ b/tests/Data/AuthorSchema.php @@ -59,11 +59,10 @@ public function getLinks($author) $links = [ Author::LINK_COMMENTS => [ - // closures for data are supported also - self::DATA => function () use ($author) { + // closures for data are supported as well + self::DATA => function () use ($author) { return isset($author->{Author::LINK_COMMENTS}) ? $author->{Author::LINK_COMMENTS} : null; }, - self::INCLUDED => true, ], ]; diff --git a/tests/Data/CommentSchema.php b/tests/Data/CommentSchema.php index 415da27b..113f7e2a 100644 --- a/tests/Data/CommentSchema.php +++ b/tests/Data/CommentSchema.php @@ -63,8 +63,7 @@ public function getLinks($comment) $links = [ Comment::LINK_AUTHOR => [ - self::DATA => isset($comment->{Comment::LINK_AUTHOR}) ? $comment->{Comment::LINK_AUTHOR} : null, - self::INCLUDED => true, + self::DATA => isset($comment->{Comment::LINK_AUTHOR}) ? $comment->{Comment::LINK_AUTHOR} : null, ], ]; diff --git a/tests/Data/DevSchemaProvider.php b/tests/Data/DevSchemaProvider.php index 9a75740b..17c7dfab 100644 --- a/tests/Data/DevSchemaProvider.php +++ b/tests/Data/DevSchemaProvider.php @@ -40,6 +40,11 @@ abstract class DevSchemaProvider extends SchemaProvider */ private $linkRemove = []; + /** + * @var string[] + */ + private $includePaths = []; + /** * Add to 'add to link' list. * @@ -83,13 +88,23 @@ public function linkRemove($name) } /** - * Set parser's max depth level. + * Get include paths. + * + * @return string[] + */ + public function getIncludePaths() + { + return empty($this->includePaths) === false ? $this->includePaths : parent::getIncludePaths(); + } + + /** + * Set include paths. * - * @param int $depthLevel + * @param string[] $includePaths */ - public function setDefaultParseDepth($depthLevel) + public function setIncludePaths($includePaths) { - $this->defaultParseDepth = $depthLevel; + $this->includePaths = $includePaths; } /** diff --git a/tests/Data/SiteSchema.php b/tests/Data/SiteSchema.php index 2bd0b4ae..c10588b0 100644 --- a/tests/Data/SiteSchema.php +++ b/tests/Data/SiteSchema.php @@ -16,6 +16,9 @@ * limitations under the License. */ +use \Neomerx\JsonApi\Contracts\Schema\ContainerInterface; +use \Neomerx\JsonApi\Contracts\Schema\SchemaFactoryInterface; + /** * @package Neomerx\Tests\JsonApi */ @@ -31,6 +34,21 @@ class SiteSchema extends DevSchemaProvider */ protected $baseSelfUrl = 'http://example.com/sites'; + /** + * @param SchemaFactoryInterface $factory + * @param ContainerInterface $container + */ + public function __construct(SchemaFactoryInterface $factory, ContainerInterface $container) + { + parent::__construct($factory, $container); + + $this->setIncludePaths([ + Site::LINK_POSTS, + Site::LINK_POSTS . '.' . Post::LINK_AUTHOR, + Site::LINK_POSTS . '.' . Post::LINK_COMMENTS, + ]); + } + /** * @inheritdoc */ @@ -59,10 +77,7 @@ public function getLinks($site) assert('$site instanceof '.Site::class); return [ - Site::LINK_POSTS => [ - self::DATA => $site->{Site::LINK_POSTS}, - self::INCLUDED => true, - ], + Site::LINK_POSTS => [self::DATA => $site->{Site::LINK_POSTS}], ]; } } diff --git a/tests/Document/DocumentTest.php b/tests/Document/DocumentTest.php index 8adf86cc..68576d19 100644 --- a/tests/Document/DocumentTest.php +++ b/tests/Document/DocumentTest.php @@ -126,11 +126,11 @@ public function testAddToDataArrayedShowMembers() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'meta'], 'selfUrl', - 'some-self-controller-data', true, true, false, false, + false, false )); $this->document->setResourceCompleted($resource); @@ -170,11 +170,11 @@ public function testAddToDataNotArrayedHiddenMembers() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'meta'], 'selfUrl', - 'some-self-controller-data', false, false, true, true, + true, true )); $this->document->setResourceCompleted($resource); @@ -236,7 +236,7 @@ public function testAddLinkToDataShowLinkMembers() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'peopleSelfUrl/', - 'some-self-controller-data', + false, false, false, false, @@ -250,7 +250,7 @@ public function testAddLinkToDataShowLinkMembers() ['title' => 'some title', 'body' => 'some body'], ['some' => 'comment meta'], 'commentsSelfUrl/', - 'some-self-controller-data', + false, false, false, false, @@ -268,9 +268,6 @@ public function testAddLinkToDataShowLinkMembers() true, true, true, - false, - 'some-self-controller-data', - 'some-related-controller-data', $this->schemaFactory->createPaginationLinks('/first') ); $this->document->addLinkToData($parent, $link, $resource); @@ -312,7 +309,7 @@ public function testAddLinkToDataHideLinkMembersExceptLinkage() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'selfUrl/', - 'some-self-controller-data', + false, false, false, false, @@ -326,7 +323,7 @@ public function testAddLinkToDataHideLinkMembersExceptLinkage() ['title' => 'some title', 'body' => 'some body'], ['some' => 'comment meta'], 'selfUrl/', - 'some-self-controller-data', + true, true, true, true, @@ -344,9 +341,6 @@ public function testAddLinkToDataHideLinkMembersExceptLinkage() true, false, false, - false, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addLinkToData($parent, $link, $resource); @@ -363,7 +357,7 @@ public function testAddLinkToDataHideLinkMembersExceptLinkage() }, "links" : { "link-name" : { - "linkage" : { "type" : "comments", "id" : "321" } + "linkage" : { "type" : "comments", "id" : "321", "meta" : {"some" : "comment meta"} } } } } @@ -384,7 +378,7 @@ public function testAddMultipleLinksToData() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'selfUrl/', - 'some-self-controller-data', + false, false, false, false, @@ -398,7 +392,7 @@ public function testAddMultipleLinksToData() ['title' => 'some title', 'body' => 'some body'], ['some' => 'comment meta'], 'selfUrl/', - 'some-self-controller-data', + true, true, true, true, @@ -416,9 +410,6 @@ public function testAddMultipleLinksToData() true, false, false, - false, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addLinkToData($parent, $link, $resource); @@ -437,8 +428,8 @@ public function testAddMultipleLinksToData() "links" : { "link-name" : { "linkage" : [ - { "type" : "comments", "id" : "321" }, - { "type" : "comments", "id" : "321" } + { "type" : "comments", "id" : "321", "meta" : {"some" : "comment meta"} }, + { "type" : "comments", "id" : "321", "meta" : {"some" : "comment meta"} } ] } } @@ -460,7 +451,7 @@ public function testAddLinkToDataHideLinkMembersExceptMeta() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'selfUrl', - 'some-self-controller-data', + false, false, false, false, @@ -474,7 +465,7 @@ public function testAddLinkToDataHideLinkMembersExceptMeta() ['title' => 'some title', 'body' => 'some body'], ['some' => 'comment meta'], 'selfUrl/', - 'some-self-controller-data', + true, true, true, true, @@ -492,9 +483,6 @@ public function testAddLinkToDataHideLinkMembersExceptMeta() false, true, false, - false, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addLinkToData($parent, $link, $resource); @@ -532,7 +520,7 @@ public function testAddReferenceToData() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'peopleSelfUrl', - 'some-self-controller-data', + false, false, false, false, @@ -546,7 +534,7 @@ public function testAddReferenceToData() ['title' => 'some title', 'body' => 'some body'], ['some' => 'comment meta'], 'ommentsSelfUrl', - 'some-self-controller-data', + false, false, false, false, @@ -564,9 +552,6 @@ public function testAddReferenceToData() true, true, false, - true, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addReferenceToData($parent, $link, $resource); @@ -602,7 +587,7 @@ public function testAddEmptyLinkToData() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'selfUrl', - 'some-self-controller-data', + false, false, false, false, @@ -620,9 +605,6 @@ public function testAddEmptyLinkToData() false, true, false, - false, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addEmptyLinkToData($parent, $link); @@ -658,7 +640,7 @@ public function testAddNullLinkToData() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'selfUrl', - 'some-self-controller-data', + false, false, false, false, @@ -676,9 +658,6 @@ public function testAddNullLinkToData() false, true, false, - false, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addNullLinkToData($parent, $link); @@ -714,11 +693,11 @@ public function testAddToIncludedShowMembers() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'meta'], 'selfUrl', - 'some-self-controller-data', false, false, true, true, + true, true )); $this->document->setResourceCompleted($resource); @@ -759,11 +738,11 @@ public function testAddToIncludedHideMembers() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'meta'], 'selfUrl', - 'some-self-controller-data', true, true, false, false, + false, false )); $this->document->setResourceCompleted($resource); @@ -798,11 +777,11 @@ public function testAddLinkToIncludedShowLinkMembers() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'peopleSelfUrl/', - 'some-self-controller-data', false, false, true, true, + true, true )); $resource = $this->schemaFactory->createResourceObject( @@ -812,7 +791,7 @@ public function testAddLinkToIncludedShowLinkMembers() ['title' => 'some title', 'body' => 'some body'], ['some' => 'comment meta'], 'commentsSelfUrl/', - 'some-self-controller-data', + true, true, true, true, @@ -830,9 +809,6 @@ public function testAddLinkToIncludedShowLinkMembers() true, true, false, - true, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addLinkToIncluded($parent, $link, $resource); @@ -854,7 +830,7 @@ public function testAddLinkToIncludedShowLinkMembers() "self" : "peopleSelfUrl/selfSubUrl", "related" : "peopleSelfUrl/relatedSubUrl", "meta" : { "some" : "comment meta" }, - "linkage" : { "type" : "comments", "id" : "321" } + "linkage" : { "type" : "comments", "id" : "321", "meta" : {"some" : "comment meta"} } } }, "meta" : { @@ -878,11 +854,11 @@ public function testAddLinkToIncludedHideMembersForLinkedResource() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'peopleSelfUrl/', - 'some-self-controller-data', false, false, true, true, + true, true )); $resource = $this->schemaFactory->createResourceObject( @@ -892,7 +868,7 @@ public function testAddLinkToIncludedHideMembersForLinkedResource() ['title' => 'some title', 'body' => 'some body'], ['some' => 'comment meta'], 'commentsSelfUrl/', - 'some-self-controller-data', + true, true, true, true, @@ -910,9 +886,6 @@ public function testAddLinkToIncludedHideMembersForLinkedResource() true, false, false, - true, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addLinkToIncluded($parent, $link, $resource); @@ -931,7 +904,7 @@ public function testAddLinkToIncludedHideMembersForLinkedResource() "links" : { "self" : "peopleSelfUrl/", "link-name" : { - "linkage" : { "type" : "comments", "id" : "321" } + "linkage" : { "type" : "comments", "id" : "321", "meta" : {"some" : "comment meta"} } } }, "meta" : { @@ -955,11 +928,11 @@ public function testAddEmptyLinkToIncluded() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'peopleSelfUrl/', - 'some-self-controller-data', false, false, true, true, + true, true )); $link = $this->schemaFactory->createLinkObject( @@ -973,9 +946,6 @@ public function testAddEmptyLinkToIncluded() true, true, false, - true, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addEmptyLinkToIncluded($parent, $link); @@ -1016,11 +986,11 @@ public function testAddNullLinkToIncluded() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'peopleSelfUrl/', - 'some-self-controller-data', false, false, true, true, + true, true )); $link = $this->schemaFactory->createLinkObject( @@ -1034,9 +1004,6 @@ public function testAddNullLinkToIncluded() true, true, false, - true, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addNullLinkToIncluded($parent, $link); @@ -1077,11 +1044,11 @@ public function testAddReferenceLinkToIncluded() ['firstName' => 'John', 'lastName' => 'Dow'], ['some' => 'author meta'], 'peopleSelfUrl/', - 'some-self-controller-data', false, false, true, true, + true, true )); $link = $this->schemaFactory->createLinkObject( @@ -1095,9 +1062,6 @@ public function testAddReferenceLinkToIncluded() true, true, false, - true, - 'some-self-controller-data', - 'some-related-controller-data', null ); $this->document->addReferenceToIncluded($parent, $link); @@ -1138,7 +1102,7 @@ public function testAddTypeAndIdOnly() [], null, '', - null, + false, false, false, false, diff --git a/tests/Encoder/EncodeIncludedObjectsTest.php b/tests/Encoder/EncodeIncludedObjectsTest.php index 49455a62..3c0a4310 100644 --- a/tests/Encoder/EncodeIncludedObjectsTest.php +++ b/tests/Encoder/EncodeIncludedObjectsTest.php @@ -89,7 +89,7 @@ public function testEncodeWithIncludedObjects() }, Post::class => function ($factory, $container) { $schema = new PostSchema($factory, $container); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::INCLUDED, true); + $schema->setIncludePaths([Post::LINK_COMMENTS]); return $schema; }, ])->encode($this->post); @@ -153,12 +153,7 @@ public function testEncodeWithRecursiveIncludedObjects() $actual = Encoder::instance([ Author::class => AuthorSchema::class, Comment::class => CommentSchema::class, - Post::class => function ($factory, $container) { - $schema = new PostSchema($factory, $container); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::INCLUDED, true); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::INCLUDED, true); - return $schema; - }, + Post::class => PostSchema::class, Site::class => SiteSchema::class, ])->encode($this->site, null, null, new EncodingParameters( // include only this relation @@ -229,12 +224,7 @@ public function testEncodeWithNullAndEmptyLinks() $actual = Encoder::instance([ Author::class => AuthorSchema::class, Comment::class => CommentSchema::class, - Post::class => function ($factory, $container) { - $schema = new PostSchema($factory, $container); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::INCLUDED, true); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::INCLUDED, true); - return $schema; - }, + Post::class => PostSchema::class, Site::class => SiteSchema::class, ])->encode($this->site); @@ -289,12 +279,7 @@ public function testEncodeDuplicatesWithCyclicDeps() $actual = Encoder::instance([ Author::class => AuthorSchema::class, Comment::class => CommentSchema::class, - Post::class => function ($factory, $container) { - $schema = new PostSchema($factory, $container); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::INCLUDED, true); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::INCLUDED, true); - return $schema; - }, + Post::class => PostSchema::class, Site::class => SiteSchema::class, ])->encode($this->site); @@ -349,9 +334,7 @@ public function testEncodeLinksAsRefs() Post::class => function ($factory, $container) { $schema = new PostSchema($factory, $container); $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::SHOW_AS_REF, true); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::RELATED_CONTROLLER, 'author-controller-info'); $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::SHOW_AS_REF, true); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::RELATED_CONTROLLER, 'comments-controller-info'); return $schema; }, Site::class => SiteSchema::class, @@ -402,23 +385,14 @@ public function testEncodeLinksAsRefs() public function testEncodeLinkNonIncludableWithIncludableLinks() { $actual = Encoder::instance([ - Author::class => function ($factory, $container) { - $schema = new AuthorSchema($factory, $container); - $schema->linkAddTo(Author::LINK_COMMENTS, AuthorSchema::INCLUDED, true); - return $schema; - }, - Comment::class => function ($factory, $container) { - $schema = new CommentSchema($factory, $container); - $schema->linkAddTo(Comment::LINK_AUTHOR, CommentSchema::INCLUDED, true); - return $schema; - }, - Post::class => function ($factory, $container) { - $schema = new PostSchema($factory, $container); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::INCLUDED, false); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::INCLUDED, false); + Author::class => AuthorSchema::class, + Comment::class => CommentSchema::class, + Post::class => PostSchema::class, + Site::class => function ($factory, $container) { + $schema = new SiteSchema($factory, $container); + $schema->setIncludePaths([Site::LINK_POSTS]); return $schema; }, - Site::class => SiteSchema::class, ])->encode($this->site); $expected = << AuthorSchema::class, - Comment::class => function ($factory, $container) { - $schema = new CommentSchema($factory, $container); - $schema->linkRemove(Comment::LINK_AUTHOR); - return $schema; - }, + Comment::class => CommentSchema::class, Post::class => function ($factory, $container) { $schema = new PostSchema($factory, $container); $schema->linkAddTo( diff --git a/tests/Encoder/EncodeSparseAndFieldSetsTest.php b/tests/Encoder/EncodeSparseAndFieldSetsTest.php index d0ec5a33..b713f0ef 100644 --- a/tests/Encoder/EncodeSparseAndFieldSetsTest.php +++ b/tests/Encoder/EncodeSparseAndFieldSetsTest.php @@ -85,12 +85,7 @@ public function testEncodeWithRecursiveIncludedObjects() $actual = Encoder::instance([ Author::class => AuthorSchema::class, Comment::class => CommentSchema::class, - Post::class => function ($factory, $container) { - $schema = new PostSchema($factory, $container); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::INCLUDED, true); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::INCLUDED, true); - return $schema; - }, + Post::class => PostSchema::class, Site::class => SiteSchema::class, ])->encode($this->site, null, null, new EncodingParameters( [ @@ -180,12 +175,7 @@ public function testEncodeOnlyFieldSets() $actual = Encoder::instance([ Author::class => AuthorSchema::class, Comment::class => CommentSchema::class, - Post::class => function ($factory, $container) { - $schema = new PostSchema($factory, $container); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::INCLUDED, true); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::INCLUDED, true); - return $schema; - }, + Post::class => PostSchema::class, Site::class => SiteSchema::class, ])->encode($this->site, null, null, new EncodingParameters( null, @@ -212,6 +202,13 @@ public function testEncodeOnlyFieldSets() } }, "included" : [{ + "type" : "people", + "id" : "9", + "attributes" : { + "first_name" : "Dan", + "last_name" : "Gebhardt" + } + }, { "type" : "comments", "id" : "5", "links" : { @@ -223,13 +220,6 @@ public function testEncodeOnlyFieldSets() "links" : { "self" : "http://example.com/comments/12" } - }, { - "type" : "people", - "id" : "9", - "attributes" : { - "first_name" : "Dan", - "last_name" : "Gebhardt" - } }, { "type" : "posts", "id" : "1" diff --git a/tests/Encoder/EncoderTest.php b/tests/Encoder/EncoderTest.php index 8eb043f9..b1ec9fd8 100644 --- a/tests/Encoder/EncoderTest.php +++ b/tests/Encoder/EncoderTest.php @@ -18,12 +18,10 @@ use \Neomerx\JsonApi\Encoder\Encoder; use \Neomerx\Tests\JsonApi\Data\Post; -use \Neomerx\Tests\JsonApi\Data\Site; use \Neomerx\Tests\JsonApi\Data\Author; use \Neomerx\Tests\JsonApi\Data\Comment; use \Neomerx\Tests\JsonApi\BaseTestCase; use \Neomerx\Tests\JsonApi\Data\PostSchema; -use \Neomerx\Tests\JsonApi\Data\SiteSchema; use \Neomerx\Tests\JsonApi\Data\AuthorSchema; use \Neomerx\Tests\JsonApi\Data\CommentSchema; use \Neomerx\JsonApi\Parameters\EncodingParameters; @@ -97,7 +95,7 @@ public function testEncodeDuplicatesWithCircularReferencesInData() $endcoder = Encoder::instance([ Author::class => function ($factory, $container) { $schema = new AuthorSchema($factory, $container); - $schema->linkAddTo(Author::LINK_COMMENTS, AuthorSchema::INCLUDED, false); + $schema->setIncludePaths([]); return $schema; }, ]); @@ -154,11 +152,7 @@ public function testEncodeDuplicatesWithRelationFieldSetFilter() Comment::instance(12, 'I like XML better', $author), ]; $endcoder = Encoder::instance([ - Author::class => function ($factory, $container) { - $schema = new AuthorSchema($factory, $container); - $schema->linkAddTo(Author::LINK_COMMENTS, AuthorSchema::INCLUDED, false); - return $schema; - }, + Author::class => AuthorSchema::class, Comment::class => CommentSchema::class, ]); @@ -255,9 +249,7 @@ public function testEncodeLinkAsReference() Post::class => function ($factory, $container) { $schema = new PostSchema($factory, $container); $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::SHOW_AS_REF, true); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::RELATED_CONTROLLER, 'author-controller-info'); $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::SHOW_AS_REF, true); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::RELATED_CONTROLLER, 'comments-controller-info'); return $schema; }, ])->encode($this->getStandardPost()); @@ -324,63 +316,6 @@ public function testEncodeEmptyLinks() $this->assertEquals($expected, $actual); } - /** - * Test encode nested included objects with cyclic dependencies and sparse support. - */ - public function testEncodeWithMaxDepthLevel() - { - $author = Author::instance(9, 'Dan', 'Gebhardt'); - $comments = [ - Comment::instance(5, 'First!', $author), - Comment::instance(12, 'I like XML better', $author), - ]; - $author->{Author::LINK_COMMENTS} = $comments; - $post = Post::instance( - 1, - 'JSON API paints my bikeshed!', - 'Outside every fat man there was an even fatter man trying to close in', - $author, - $comments - ); - $site = Site::instance(2, 'site name', [$post]); - - $actual = Encoder::instance([ - Author::class => AuthorSchema::class, - Comment::class => CommentSchema::class, - Post::class => PostSchema::class, - Site::class => function ($factory, $container) { - $schema = new SiteSchema($factory, $container); - $schema->setDefaultParseDepth(1); - return $schema; - }, - ])->encode($site); - - $expected = <<assertEquals($expected, $actual); - } - /** * @return Post */ diff --git a/tests/Encoder/Parser/ParserTest.php b/tests/Encoder/Parser/ParserTest.php index 10f12bec..234fbb34 100644 --- a/tests/Encoder/Parser/ParserTest.php +++ b/tests/Encoder/Parser/ParserTest.php @@ -86,9 +86,7 @@ protected function setUp() Post::class => function ($factory, $container) { $schema = new PostSchema($factory, $container); $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::SHOW_AS_REF, true); - $schema->linkAddTo(Post::LINK_AUTHOR, PostSchema::RELATED_CONTROLLER, 'author-controller-info'); $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::SHOW_AS_REF, true); - $schema->linkAddTo(Post::LINK_COMMENTS, PostSchema::RELATED_CONTROLLER, 'comments-controller-info'); return $schema; }, ]; diff --git a/tests/Encoder/SchemaTest.php b/tests/Encoder/SchemaTest.php index c351db9d..784bc8f7 100644 --- a/tests/Encoder/SchemaTest.php +++ b/tests/Encoder/SchemaTest.php @@ -16,7 +16,6 @@ * limitations under the License. */ -use \ReflectionMethod; use \Neomerx\Tests\JsonApi\BaseTestCase; use \Neomerx\JsonApi\Schema\SchemaFactory; use \Neomerx\Tests\JsonApi\Data\DummySchema; @@ -42,20 +41,6 @@ protected function setUp() $this->schema = new DummySchema($schemaFactory, $schemaFactory->createContainer()); } - /** - * Test getNotEmptyValue. - */ - public function testGetNotEmptyValue() - { - $reflectionMethod = new ReflectionMethod(DummySchema::class, 'getNotEmptyValue'); - $reflectionMethod->setAccessible(true); - - $this->assertEquals('default', $reflectionMethod->invoke($this->schema, [], 'key', 'default')); - $this->assertEquals('default', $reflectionMethod->invoke($this->schema, ['key' => null], 'key', 'default')); - $this->assertEquals('default', $reflectionMethod->invoke($this->schema, ['key' => ''], 'key', 'default')); - $this->assertEquals('value', $reflectionMethod->invoke($this->schema, ['key' => 'value'], 'key', 'default')); - } - public function testGetLinks() { $this->assertEmpty($this->schema->getLinks(null)); diff --git a/tests/Exceptions/RenderContainerTest.php b/tests/Exceptions/RenderContainerTest.php index e39ee53d..af43bf79 100644 --- a/tests/Exceptions/RenderContainerTest.php +++ b/tests/Exceptions/RenderContainerTest.php @@ -16,12 +16,18 @@ * limitations under the License. */ +use \Mockery; use \Exception; use \LogicException; +use \Mockery\MockInterface; use \InvalidArgumentException; +use Neomerx\JsonApi\Document\Error; +use Neomerx\JsonApi\Encoder\Encoder; use \Neomerx\Tests\JsonApi\BaseTestCase; use \Neomerx\JsonApi\Exceptions\RenderContainer; use \Neomerx\JsonApi\Contracts\Exceptions\RenderContainerInterface; +use \Neomerx\JsonApi\Contracts\Integration\NativeResponsesInterface; +use \Neomerx\JsonApi\Contracts\Parameters\SupportedExtensionsInterface; /** * @package Neomerx\Tests\JsonApi @@ -36,6 +42,11 @@ class RenderContainerTest extends BaseTestCase */ private $container; + /** + * @var MockInterface + */ + private $mockResponses; + /** * Set up tests. */ @@ -43,11 +54,18 @@ protected function setUp() { parent::setUp(); - $responseClosure = function ($statusCode) { - return 'error: '. $statusCode; + $mockSupportedExtensions = Mockery::mock(SupportedExtensionsInterface::class); + $mockSupportedExtensions->shouldReceive('getExtensions')->zeroOrMoreTimes()->withNoArgs()->andReturn([]); + $extensionsClosure = function () use ($mockSupportedExtensions) { + return $mockSupportedExtensions; }; - $this->container = new RenderContainer($responseClosure, self::DEFAULT_CODE); + $this->mockResponses = Mockery::mock(NativeResponsesInterface::class); + + /** @var NativeResponsesInterface $mockResponses */ + $mockResponses = $this->mockResponses; + + $this->container = new RenderContainer($mockResponses, $extensionsClosure, self::DEFAULT_CODE); } /** @@ -55,6 +73,10 @@ protected function setUp() */ public function testGetRenderForUnknownException() { + $this->mockResponses->shouldReceive('createResponse')->once() + ->withArgs([null, self::DEFAULT_CODE, Mockery::any()]) + ->andReturn('error: '. self::DEFAULT_CODE); + // we haven't registered any renders yet so any exception will be unknown $this->assertNotNull($render = $this->container->getRender(new Exception())); @@ -66,6 +88,10 @@ public function testGetRenderForUnknownException() */ public function testGetRenderForKnownException() { + $this->mockResponses->shouldReceive('createResponse')->once() + ->withArgs([null, self::DEFAULT_CODE, Mockery::any()]) + ->andReturn('error: '. self::DEFAULT_CODE); + $customRender = function ($arg1, $arg2, $arg3) { return $arg1 . ' ' . $arg2 . ' ' . $arg3; }; @@ -81,20 +107,52 @@ public function testGetRenderForKnownException() /** * Test register exception mapping to status codes. */ - public function testRegisterMapping() + public function testRegisterHttpCodeMapping() { - $this->container->registerMapping([ + $this->container->registerHttpCodeMapping([ InvalidArgumentException::class => 123, LogicException::class => 456, ]); + $this->mockResponses->shouldReceive('createResponse')->once() + ->withArgs([null, 123, Mockery::any()]) + ->andReturn('error: '. 123); $this->assertNotNull($render = $this->container->getRender(new InvalidArgumentException())); $this->assertEquals('error: '. 123, $render()); + $this->mockResponses->shouldReceive('createResponse')->once() + ->withArgs([null, 456, Mockery::any()]) + ->andReturn('error: '. 456); $this->assertNotNull($render = $this->container->getRender(new LogicException())); $this->assertEquals('error: '. 456, $render()); + $this->mockResponses->shouldReceive('createResponse')->once() + ->withArgs([null, self::DEFAULT_CODE, Mockery::any()]) + ->andReturn('error: '. self::DEFAULT_CODE); $this->assertNotNull($render = $this->container->getRender(new Exception())); $this->assertEquals('error: '. self::DEFAULT_CODE, $render()); } + + /** + * Test register exception mapping for JSON API Errors. + */ + public function testRegisterJsonApiErrorMapping() + { + $this->container->registerJsonApiErrorMapping([ + InvalidArgumentException::class => 123, + ]); + + $title = 'Error title'; + $error = new Error(null, null, null, null, $title); + $errorDocument = Encoder::instance([])->error($error); + + $this->mockResponses->shouldReceive('createResponse')->once() + ->withArgs([Mockery::type('string'), 123, Mockery::any()]) + ->andReturn($errorDocument); + $this->assertNotNull($render = $this->container->getRender(new InvalidArgumentException())); + + // let's assume our exception can provide JSON API Error information somehow. + + $this->assertEquals($errorDocument, $render([$error])); + } } diff --git a/tests/Schema/FactoryTest.php b/tests/Schema/FactoryTest.php index 668b7f0a..8c2c7362 100644 --- a/tests/Schema/FactoryTest.php +++ b/tests/Schema/FactoryTest.php @@ -62,12 +62,12 @@ public function testCreateResourceObject() $attributes = ['firstName' => 'John', 'lastName' => 'Dow'], $meta = ['some' => 'author meta'], $selfUrl = 'peopleSelfUrl/', - $selfCtrlData = 'some-self-controller-data', $isShowSelf = false, $isShowMeta = false, $isShowSelfInIncluded = true, $isShowLinksInIncluded = true, - $isShowMetaInIncluded = true + $isShowMetaInIncluded = true, + $isShowMetaInLinkage = true )); $this->assertEquals($isInArray, $resource->isInArray()); @@ -76,12 +76,12 @@ public function testCreateResourceObject() $this->assertEquals($attributes, $resource->getAttributes()); $this->assertEquals($meta, $resource->getMeta()); $this->assertEquals($selfUrl, $resource->getSelfUrl()); - $this->assertEquals($selfCtrlData, $resource->getSelfControllerData()); $this->assertEquals($isShowSelf, $resource->isShowSelf()); $this->assertEquals($isShowMeta, $resource->isShowMeta()); $this->assertEquals($isShowSelfInIncluded, $resource->isShowSelfInIncluded()); $this->assertEquals($isShowLinksInIncluded, $resource->isShowLinksInIncluded()); $this->assertEquals($isShowMetaInIncluded, $resource->isShowMetaInIncluded()); + $this->assertEquals($isShowMetaInLinkage, $resource->isShowMetaInLinkage()); } /** @@ -100,9 +100,6 @@ public function testCreateLinkObject() $isShowLinkage = true, $isShowMeta = true, $isShowPagination = true, - $isIncluded = true, - $selfControllerData = 'some-self-controller-data', - $relatedControllerData = 'some-related-controller-data', $pagination = Mockery::mock(PaginationLinksInterface::class) )); @@ -116,9 +113,6 @@ public function testCreateLinkObject() $this->assertEquals($isShowLinkage, $link->isShowLinkage()); $this->assertEquals($isShowMeta, $link->isShowMeta()); $this->assertEquals($isShowPagination, $link->isShowPagination()); - $this->assertEquals($isIncluded, $link->isShouldBeIncluded()); - $this->assertEquals($selfControllerData, $link->getSelfControllerData()); - $this->assertEquals($relatedControllerData, $link->getRelatedControllerData()); $this->assertSame($pagination, $link->getPagination()); }