Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove art4/json-api-client dependency #58

Merged
merged 1 commit into from
Jul 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

* Nothing
### Changed

* Drop art4/json-api-client dependency and validate the JSON ourselves.

## [0.20.0] - 2019-07-11

Expand Down
6 changes: 3 additions & 3 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ This package uses [Laravel Collections](https://laravel.com/docs/collections) as
## Links

All objects that can have links (i.e. document, error, item and relationship) use `Concerns/HasLinks` and thus have a `getLinks` method that returns an instance of `Links`.
This is a simple array-like object with key-value pairs which are in turn an instance of `Link`.
This is a simple array-like object with key-value pairs which are in turn an instance of `Link` or `null`.

### Example

Expand Down Expand Up @@ -432,7 +432,7 @@ If a response does not have a successful status code (2xx) and does not have a b
#### Non 2xx request with invalid JSON:API body

If a response does not have a successful status code (2xx) and does have a body, it is parsed as if it's a JSON:API document.
If the response can not be parsed as such document, a `\Art4\JsonApiClient\Exception\ValidationException` will be thrown.
If the response can not be parsed as such document, a `ValidationException` will be thrown.

#### Non 2xx request with valid JSON:API body

Expand Down Expand Up @@ -466,7 +466,7 @@ This can be a plain `Document` when there is no data, an `ItemDocument` for an i

The `DocumentClient` follows the following steps internally:
1. Send the request using your HTTP client;
2. Use [art4/json-api-client](https://github.com/art4/json-api-client) to parse and validate the response;
2. Use `ResponseParser` to parse and validate the response;
3. Create the correct document instance;
4. Hydrate every item by using the item model registered with the `TypeMapper` or a `\Swis\JsonApi\Client\Item` as fallback;
5. Hydrate all relationships;
Expand Down
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"require": {
"php": ">=7.1.3",
"ext-json": "*",
"art4/json-api-client": "^0.9.1",
"illuminate/support": "5.5.*|5.6.*|5.7.*|5.8.*",
"jenssegers/model": "^1.1",
"php-http/client-implementation": "^1.0",
Expand Down
7 changes: 7 additions & 0 deletions src/Exceptions/Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Swis\JsonApi\Client\Exceptions;

interface Exception
{
}
7 changes: 7 additions & 0 deletions src/Exceptions/ValidationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Swis\JsonApi\Client\Exceptions;

class ValidationException extends \InvalidArgumentException implements Exception
{
}
7 changes: 7 additions & 0 deletions src/Interfaces/ItemInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ public function getAttribute($key);
*/
public function setAttribute($key, $value);

/**
* @param $key
*
* @return bool
*/
public function hasAttribute($key): bool;

/**
* @return bool
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public function getAttribute($key)
}

/**
* @param string $key
* @param $key
*
* @return bool
*/
Expand Down
4 changes: 2 additions & 2 deletions src/Links.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ public function offsetUnset($offset)
public function toArray()
{
return array_map(
function (Link $link) {
return $link->toArray();
static function (?Link $link) {
return $link ? $link->toArray() : null;
},
$this->links
);
Expand Down
15 changes: 9 additions & 6 deletions src/Parsers/CollectionParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

namespace Swis\JsonApi\Client\Parsers;

use Art4\JsonApiClient\ResourceCollectionInterface;
use Art4\JsonApiClient\ResourceItemInterface;
use Swis\JsonApi\Client\Collection;
use Swis\JsonApi\Client\Exceptions\ValidationException;

/**
* @internal
Expand All @@ -25,15 +24,19 @@ public function __construct(ItemParser $itemParser)
}

/**
* @param \Art4\JsonApiClient\ResourceCollectionInterface $jsonApiCollection
* @param mixed $data
*
* @return \Swis\JsonApi\Client\Collection
*/
public function parse(ResourceCollectionInterface $jsonApiCollection): Collection
public function parse($data): Collection
{
return Collection::make($jsonApiCollection->asArray())
if (!is_array($data)) {
throw new ValidationException(sprintf('ResourceCollection has to be an array, "%s" given.', gettype($data)));
}

return Collection::make($data)
->map(
function (ResourceItemInterface $item) {
function ($item) {
return $this->itemParser->parse($item);
}
);
Expand Down
176 changes: 73 additions & 103 deletions src/Parsers/DocumentParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,19 @@

namespace Swis\JsonApi\Client\Parsers;

use Art4\JsonApiClient\DocumentInterface as Art4JsonApiDocumentInterface;
use Art4\JsonApiClient\ResourceCollectionInterface;
use Art4\JsonApiClient\ResourceItemInterface;
use Art4\JsonApiClient\Utils\Manager as Art4JsonApiClientManager;
use Swis\JsonApi\Client\Collection;
use Swis\JsonApi\Client\CollectionDocument;
use Swis\JsonApi\Client\Document;
use Swis\JsonApi\Client\ErrorCollection;
use Swis\JsonApi\Client\Exceptions\ValidationException;
use Swis\JsonApi\Client\Interfaces\DocumentInterface;
use Swis\JsonApi\Client\Interfaces\DocumentParserInterface;
use Swis\JsonApi\Client\Interfaces\ItemInterface;
use Swis\JsonApi\Client\Interfaces\ManyRelationInterface;
use Swis\JsonApi\Client\Interfaces\OneRelationInterface;
use Swis\JsonApi\Client\ItemDocument;
use Swis\JsonApi\Client\Jsonapi;
use Swis\JsonApi\Client\Links;
use Swis\JsonApi\Client\Meta;

class DocumentParser implements DocumentParserInterface
{
/**
* @var \Art4\JsonApiClient\Utils\Manager
*/
private $manager;

/**
* @var \Swis\JsonApi\Client\Parsers\ItemParser
*/
Expand All @@ -38,9 +26,9 @@ class DocumentParser implements DocumentParserInterface
private $collectionParser;

/**
* @var \Swis\JsonApi\Client\Parsers\ErrorsParser
* @var \Swis\JsonApi\Client\Parsers\ErrorCollectionParser
*/
private $errorsParser;
private $errorCollectionParser;

/**
* @var \Swis\JsonApi\Client\Parsers\LinksParser
Expand All @@ -58,27 +46,24 @@ class DocumentParser implements DocumentParserInterface
private $metaParser;

/**
* @param \Art4\JsonApiClient\Utils\Manager $manager
* @param \Swis\JsonApi\Client\Parsers\ItemParser $itemParser
* @param \Swis\JsonApi\Client\Parsers\CollectionParser $collectionParser
* @param \Swis\JsonApi\Client\Parsers\ErrorsParser $errorsParser
* @param \Swis\JsonApi\Client\Parsers\LinksParser $linksParser
* @param \Swis\JsonApi\Client\Parsers\JsonapiParser $jsonapiParser
* @param \Swis\JsonApi\Client\Parsers\MetaParser $metaParser
* @param \Swis\JsonApi\Client\Parsers\ItemParser $itemParser
* @param \Swis\JsonApi\Client\Parsers\CollectionParser $collectionParser
* @param \Swis\JsonApi\Client\Parsers\ErrorCollectionParser $errorCollectionParser
* @param \Swis\JsonApi\Client\Parsers\LinksParser $linksParser
* @param \Swis\JsonApi\Client\Parsers\JsonapiParser $jsonapiParser
* @param \Swis\JsonApi\Client\Parsers\MetaParser $metaParser
*/
public function __construct(
Art4JsonApiClientManager $manager,
ItemParser $itemParser,
CollectionParser $collectionParser,
ErrorsParser $errorsParser,
ErrorCollectionParser $errorCollectionParser,
LinksParser $linksParser,
JsonapiParser $jsonapiParser,
MetaParser $metaParser
) {
$this->manager = $manager;
$this->itemParser = $itemParser;
$this->collectionParser = $collectionParser;
$this->errorsParser = $errorsParser;
$this->errorCollectionParser = $errorCollectionParser;
$this->linksParser = $linksParser;
$this->jsonapiParser = $jsonapiParser;
$this->metaParser = $metaParser;
Expand All @@ -91,41 +76,82 @@ public function __construct(
*/
public function parse(string $json): DocumentInterface
{
/** @var \Art4\JsonApiClient\DocumentInterface $jsonApiDocument */
$jsonApiDocument = $this->manager->parse($json);

return $this->getDocument($jsonApiDocument)
->setLinks($this->parseLinks($jsonApiDocument))
->setErrors($this->parseErrors($jsonApiDocument))
->setMeta($this->parseMeta($jsonApiDocument))
->setJsonapi($this->parseJsonapi($jsonApiDocument));
$data = $this->decodeJson($json);

if (!is_object($data)) {
throw new ValidationException(sprintf('Document has to be an object, "%s" given.', gettype($data)));
}
if (!property_exists($data, 'data') && !property_exists($data, 'errors') && !property_exists($data, 'meta')) {
throw new ValidationException('Document MUST contain at least one of the following properties: `data`, `errors`, `meta`.');
}
if (property_exists($data, 'data') && property_exists($data, 'errors')) {
throw new ValidationException('The properties `data` and `errors` MUST NOT coexist in Document.');
}
if (!property_exists($data, 'data') && property_exists($data, 'included')) {
throw new ValidationException('If Document does not contain a `data` property, the `included` property MUST NOT be present either.');
}
if (property_exists($data, 'data') && !is_object($data->data) && !is_array($data->data) && $data->data !== null) {
throw new ValidationException(sprintf('Document property "data" has to be null, an array or an object, "%s" given.', gettype($data)));
}

$document = $this->getDocument($data);

if (property_exists($data, 'links')) {
$document->setLinks($this->linksParser->parse($data->links));
}

if (property_exists($data, 'errors')) {
$document->setErrors($this->errorCollectionParser->parse($data->errors));
}

if (property_exists($data, 'meta')) {
$document->setMeta($this->metaParser->parse($data->meta));
}

if (property_exists($data, 'jsonapi')) {
$document->setJsonapi($this->jsonapiParser->parse($data->jsonapi));
}

return $document;
}

/**
* @param string $json
*
* @return mixed
*/
private function decodeJson(string $json)
{
$data = json_decode($json, false);

if (json_last_error() !== JSON_ERROR_NONE) {
throw new ValidationException(sprintf('Unable to parse JSON data: %s', json_last_error_msg()), json_last_error());
}

return $data;
}

/**
* @param \Art4\JsonApiClient\DocumentInterface $jsonApiDocument
* @param mixed $data
*
* @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
*/
private function getDocument(Art4JsonApiDocumentInterface $jsonApiDocument): DocumentInterface
private function getDocument($data): DocumentInterface
{
if (!$jsonApiDocument->has('data')) {
if (!property_exists($data, 'data') || $data->data === null) {
return new Document();
}

$data = $jsonApiDocument->get('data');

if ($data instanceof ResourceItemInterface) {
$document = (new ItemDocument())
->setData($this->itemParser->parse($data));
} elseif ($data instanceof ResourceCollectionInterface) {
if (is_array($data->data)) {
$document = (new CollectionDocument())
->setData($this->collectionParser->parse($data));
->setData($this->collectionParser->parse($data->data));
} else {
throw new \DomainException('Document data is not a Collection or an Item');
$document = (new ItemDocument())
->setData($this->itemParser->parse($data->data));
}

if ($jsonApiDocument->has('included')) {
$document->setIncluded($this->collectionParser->parse($jsonApiDocument->get('included')));
if (property_exists($data, 'included')) {
$document->setIncluded($this->collectionParser->parse($data->included));
}

$allItems = Collection::wrap($document->getData())
Expand Down Expand Up @@ -196,60 +222,4 @@ private function getItemKey(ItemInterface $item): string
{
return sprintf('%s:%s', $item->getType(), $item->getId());
}

/**
* @param \Art4\JsonApiClient\DocumentInterface $document
*
* @return \Swis\JsonApi\Client\Links|null
*/
private function parseLinks(Art4JsonApiDocumentInterface $document): ?Links
{
if (!$document->has('links')) {
return null;
}

return $this->linksParser->parse($document->get('links')->asArray());
}

/**
* @param \Art4\JsonApiClient\DocumentInterface $document
*
* @return \Swis\JsonApi\Client\ErrorCollection
*/
private function parseErrors(Art4JsonApiDocumentInterface $document): ErrorCollection
{
if (!$document->has('errors')) {
return new ErrorCollection();
}

return $this->errorsParser->parse($document->get('errors'));
}

/**
* @param \Art4\JsonApiClient\DocumentInterface $document
*
* @return \Swis\JsonApi\Client\Meta|null
*/
private function parseMeta(Art4JsonApiDocumentInterface $document): ?Meta
{
if (!$document->has('meta')) {
return null;
}

return $this->metaParser->parse($document->get('meta'));
}

/**
* @param \Art4\JsonApiClient\DocumentInterface $document
*
* @return \Swis\JsonApi\Client\Jsonapi|null
*/
private function parseJsonapi(Art4JsonApiDocumentInterface $document): ?Jsonapi
{
if (!$document->has('jsonapi')) {
return null;
}

return $this->jsonapiParser->parse($document->get('jsonapi'));
}
}
Loading