Skip to content

Commit

Permalink
Merge pull request #58 from swisnl/feature/remove-art4-json-api-client
Browse files Browse the repository at this point in the history
Remove art4/json-api-client dependency
  • Loading branch information
JaZo authored Jul 22, 2019
2 parents 52b2eab + ac48f18 commit d5cfd4d
Show file tree
Hide file tree
Showing 26 changed files with 1,506 additions and 618 deletions.
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

0 comments on commit d5cfd4d

Please sign in to comment.