Skip to content

Commit

Permalink
Merge pull request #38 from bedita/feat/api-formatter
Browse files Browse the repository at this point in the history
ApiFormatterComponent
  • Loading branch information
stefanorosanelli authored Apr 15, 2021
2 parents c197d47 + b93b1c6 commit 32dbd82
Show file tree
Hide file tree
Showing 2 changed files with 294 additions and 0 deletions.
103 changes: 103 additions & 0 deletions src/Controller/Component/ApiFormatterComponent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);

/**
* BEdita, API-first content management framework
* Copyright 2021 Atlas Srl, ChannelWeb Srl, Chialab Srl
*
* This file is part of BEdita: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
*/

namespace BEdita\WebTools\Controller\Component;

use Cake\Collection\Collection;
use Cake\Controller\Component;
use Cake\Utility\Hash;

/**
* Component class to format API response data.
*/
class ApiFormatterComponent extends Component
{
/**
* Embed included data into relationships.
*
* @param array $response The response from API
* @return array
*/
public function embedIncluded(array $response): array
{
$data = (array)Hash::get($response, 'data');
if (empty($data)) {
return $response;
}

$included = (array)Hash::get($response, 'included');
if (empty($included)) {
return $response;
}

$included = collection($included);
if (!Hash::numeric(array_keys($data))) {
$response['data'] = $this->addIncluded($data, $included);

return $response;
}

foreach ($data as &$d) {
$d = $this->addIncluded($d, $included);
}
unset($d);

$response['data'] = $data;

return $response;
}

/**
* Add included data to main resource.
*
* @param array $resource The resource.
* @param \Cake\Collection\Collection $included The included collection.
* @return array
*/
protected function addIncluded(array $resource, Collection $included): array
{
foreach ($resource['relationships'] as &$relation) {
if (empty($relation['data'])) {
continue;
}

$relation['data'] = $this->extractFromIncluded($included, (array)$relation['data']);
}
unset($relation);

return $resource;
}

/**
* Extract items from included starting from $relationship data.
*
* @param \Cake\Collection\Collection $included The included collection
* @param array $relationshipData Array of relationship data.
* Every item must contain 'type' and 'id'.
* @return array
*/
protected function extractFromIncluded(Collection $included, array $relationshipData): array
{
foreach ($relationshipData as &$data) {
$data = (array)$included->firstMatch([
'type' => $data['type'],
'id' => $data['id'],
]);
}
unset($data);

return $relationshipData;
}
}
191 changes: 191 additions & 0 deletions tests/TestCase/Controller/Component/ApiFormatterComponentTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?php
declare(strict_types=1);

namespace BEdita\WebTools\Test\TestCase\Controller\Component;

use BEdita\WebTools\Controller\Component\ApiFormatterComponent;
use Cake\Controller\ComponentRegistry;
use Cake\TestSuite\TestCase;

/**
* {@see \BEdita\WebTools\Controller\Component\ApiFormatterComponent} Test Case
*
* @coversDefaultClass \BEdita\WebTools\Controller\Component\ApiFormatterComponent
*/
class ApiFormatterComponentTest extends TestCase
{
/**
* Test subject
*
* @var \App\Controller\Component\ApiFormatter
*/
public $ApiFormatter;

/**
* setUp method
*
* @return void
*/
public function setUp(): void
{
parent::setUp();
$registry = new ComponentRegistry();
$this->ApiFormatter = new ApiFormatterComponent($registry);
}

/**
* tearDown method
*
* @return void
*/
public function tearDown(): void
{
unset($this->ApiFormatter);
parent::tearDown();
}

/**
* Provider for `testEmbedIncluded` method
*
* @return array
*/
public function embedIncludedProvider(): array
{
$gustavo = ['id' => 1, 'type' => 'persons', 'attributes' => ['name' => 'Gustavo'], 'relationships' => [['chief_of' => [['id' => 777, 'type' => 'universes']]]]];
$tv = ['id' => 2, 'type' => 'things', 'attributes' => ['name' => 'Television'], 'relationships' => [['part_of' => [['id' => 888, 'type' => 'furnitures']]]]];
$relationships = [
'a' => [['id' => 1, 'type' => 'persons']],
'b' => [['id' => 2, 'type' => 'things']],
];
$relationshipsWithData = [
'a' => ['data' => [['id' => 1, 'type' => 'persons']]],
'b' => ['data' => [['id' => 2, 'type' => 'things']]],
];
$relationshipsWithDataExpected = [
'a' => ['data' => [$gustavo]],
'b' => ['data' => [$tv]],
];

return [
'no data' => [
['something'],
['something'],
],
'empty data' => [
['data' => [], 'something'],
['data' => [], 'something'],
],
'empty included' => [
['data' => [['id' => 1]]],
['data' => [['id' => 1]]],
],
'non numeric keys, no data' => [
[
'data' => ['relationships' => $relationships],
'included' => [$gustavo, $tv],
],
[
'data' => ['relationships' => $relationships],
'included' => [$gustavo, $tv],
],
],
'non numeric keys + data' => [
[
'data' => ['relationships' => $relationshipsWithData],
'included' => [$gustavo, $tv],
],
[
'data' => ['relationships' => $relationshipsWithDataExpected],
'included' => [$gustavo, $tv],
],
],
'numeric keys + data' => [
[
'data' => [
[
'id' => '12',
'type' => 'images',
'attributes' => [
'title' => 'test',
],
'relationships' => [
'streams' => [
'links' => [],
'data' => [
[
'id' => 'af829cbb-c570-4282-94c3-782cf315983a',
'type' => 'streams',
],
],
],
],
],
],
'included' => [
[
'id' => 'af829cbb-c570-4282-94c3-782cf315983a',
'type' => 'streams',
'attributes' => [
'file_name' => 'test.jpg',
'mime_type' => 'image/jpeg',
],
],
],
],
[
'data' => [
[
'id' => '12',
'type' => 'images',
'attributes' => [
'title' => 'test',
],
'relationships' => [
'streams' => [
'links' => [],
'data' => [
[
'id' => 'af829cbb-c570-4282-94c3-782cf315983a',
'type' => 'streams',
'attributes' => [
'file_name' => 'test.jpg',
'mime_type' => 'image/jpeg',
],
],
],
],
],
],
],
'included' => [
[
'id' => 'af829cbb-c570-4282-94c3-782cf315983a',
'type' => 'streams',
'attributes' => [
'file_name' => 'test.jpg',
'mime_type' => 'image/jpeg',
],
],
],
],
],
];
}

/**
* Test `embedIncluded` method
*
* @param array $response The response data for test
* @param array $expected The expected resulting data
* @return void
* @covers ::embedIncluded()
* @covers ::addIncluded()
* @covers ::extractFromIncluded()
* @dataProvider embedIncludedProvider()
*/
public function testEmbedIncluded(array $response, array $expected): void
{
$actual = $this->ApiFormatter->embedIncluded($response);
static::assertEquals($expected, $actual);
}
}

0 comments on commit 32dbd82

Please sign in to comment.