diff --git a/src/Controller/Component/ApiFormatterComponent.php b/src/Controller/Component/ApiFormatterComponent.php index 603b64c..95571c6 100644 --- a/src/Controller/Component/ApiFormatterComponent.php +++ b/src/Controller/Component/ApiFormatterComponent.php @@ -169,7 +169,7 @@ protected function extractTranslatedFields(array $data, string $lang): array */ public function cleanResponse( array $response, - array $options = ['included', 'links', 'schema', 'relationships'] + array $options = ['included', 'links', 'schema', 'relationships', 'attributes' => []] ): array { return ApiTools::cleanResponse($response, $options); } diff --git a/src/Utility/ApiTools.php b/src/Utility/ApiTools.php index a2346b0..16e61b0 100644 --- a/src/Utility/ApiTools.php +++ b/src/Utility/ApiTools.php @@ -22,6 +22,47 @@ */ class ApiTools { + /** + * Remove attributes from response. + * + * @param array $response The response from api client + * @param array $keysToRemove The keys to remove + * @return array + */ + public static function removeAttributes(array $response, array $keysToRemove): array + { + if (empty($keysToRemove) || !isset($response['data'])) { + return $response; + } + + // response.data is an array representing a single entity + if (isset($response['data']['attributes'])) { + $response['data']['attributes'] = array_diff_key($response['data']['attributes'], array_flip($keysToRemove)); + + // remove attributes from included entities + if (isset($response['included'])) { + foreach ($response['included'] as $key => $entity) { + $response['included'][$key]['attributes'] = array_diff_key($entity['attributes'], array_flip($keysToRemove)); + } + } + + return $response; + } + // response.data is a list of entities + foreach ($response['data'] as $key => $entity) { + $response['data'][$key]['attributes'] = array_diff_key($entity['attributes'], array_flip($keysToRemove)); + } + + // remove attributes from included entities + if (isset($response['included'])) { + foreach ($response['included'] as $key => $entity) { + $response['included'][$key]['attributes'] = array_diff_key($entity['attributes'], array_flip($keysToRemove)); + } + } + + return $response; + } + /** * Remove included from response. * @@ -101,9 +142,14 @@ function ($k) use ($key) { */ public static function cleanResponse( array $response, - array $options = ['included', 'links', 'schema', 'relationships'] + array $options = ['included', 'links', 'schema', 'relationships', 'attributes' => []] ): array { - foreach ($options as $option) { + foreach ($options as $key => $option) { + if (is_string($key) && is_array($option)) { + $method = 'remove' . ucfirst($key); + $response = self::$method($response, $option); + continue; + } $method = 'remove' . ucfirst($option); $response = self::$method($response); } diff --git a/tests/TestCase/Controller/Component/ApiFormatterComponentTest.php b/tests/TestCase/Controller/Component/ApiFormatterComponentTest.php index b397c84..7848b7a 100644 --- a/tests/TestCase/Controller/Component/ApiFormatterComponentTest.php +++ b/tests/TestCase/Controller/Component/ApiFormatterComponentTest.php @@ -759,6 +759,7 @@ public function testCleanResponse(): void 'title' => 'gustavo supporto', 'name' => 'gustavo', 'surname' => 'supporto', + 'extra' => ['some' => 'thing'], ], 'links' => [ 'self' => 'https://api.example.org/users/1', @@ -809,6 +810,7 @@ public function testCleanResponse(): void 'type' => 'roles', 'attributes' => [ 'name' => 'admin', + 'extra' => ['any' => 'thing'], ], ], ], @@ -828,6 +830,7 @@ public function testCleanResponse(): void 'title' => 'gustavo supporto', 'name' => 'gustavo', 'surname' => 'supporto', + 'extra' => ['some' => 'thing'], ], ], ], diff --git a/tests/TestCase/Utility/ApiToolsTest.php b/tests/TestCase/Utility/ApiToolsTest.php index b970fd1..0120bcd 100644 --- a/tests/TestCase/Utility/ApiToolsTest.php +++ b/tests/TestCase/Utility/ApiToolsTest.php @@ -30,6 +30,7 @@ class ApiToolsTest extends TestCase * @return void * @covers ::cleanResponse() * @covers ::recursiveRemoveKey() + * @covers ::removeAttributes() * @covers ::removeIncluded() * @covers ::removeLinks() * @covers ::removeRelationships() @@ -46,6 +47,7 @@ public function testCleanResponse(): void 'title' => 'gustavo supporto', 'name' => 'gustavo', 'surname' => 'supporto', + 'extra' => ['some' => 'thing'], ], 'links' => [ 'self' => 'https://api.example.org/users/1', @@ -96,6 +98,7 @@ public function testCleanResponse(): void 'type' => 'roles', 'attributes' => [ 'name' => 'admin', + 'extra' => ['any' => 'thing'], ], ], ], @@ -115,6 +118,7 @@ public function testCleanResponse(): void 'title' => 'gustavo supporto', 'name' => 'gustavo', 'surname' => 'supporto', + 'extra' => ['some' => 'thing'], ], ], ], @@ -130,4 +134,301 @@ public function testCleanResponse(): void ]; static::assertEquals($expected, $actual); } + + /** + * Data provider for `testCleanResponseAttributes` test case. + * + * @return array + */ + public function cleanResponseAttributesProvider(): array + { + return [ + 'remove extra, single entity with included' => [ + [ + 'data' => [ + 'id' => 1, + 'attributes' => [ + 'uname' => 'gustavo', + 'title' => 'gustavo supporto', + 'name' => 'gustavo', + 'surname' => 'supporto', + 'extra' => ['some' => 'thing'], + ], + 'links' => [ + 'self' => 'https://api.example.org/users/1', + ], + 'relationships' => [ + 'roles' => [ + 'links' => [ + 'self' => 'https://api.example.org/users/1/relationships/roles', + 'related' => 'https://api.example.org/users/1/roles', + ], + ], + ], + ], + 'meta' => [ + 'pagination' => [ + 'page' => 1, + 'page_count' => 1, + 'page_items' => 1, + 'page_size' => 20, + 'count' => 1, + ], + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer', + ], + 'type' => [ + 'type' => 'string', + ], + 'attributes' => [ + 'type' => 'object', + ], + 'links' => [ + 'type' => 'object', + ], + 'relationships' => [ + 'type' => 'object', + ], + ], + 'required' => ['id', 'type', 'attributes'], + ], + ], + 'included' => [ + [ + 'id' => 1, + 'type' => 'roles', + 'attributes' => [ + 'name' => 'admin', + 'extra' => ['any' => 'thing'], + ], + ], + ], + ], + [ + 'attributes' => ['extra'], + ], + [ + 'data' => [ + 'id' => 1, + 'attributes' => [ + 'uname' => 'gustavo', + 'title' => 'gustavo supporto', + 'name' => 'gustavo', + 'surname' => 'supporto', + ], + 'links' => [ + 'self' => 'https://api.example.org/users/1', + ], + 'relationships' => [ + 'roles' => [ + 'links' => [ + 'self' => 'https://api.example.org/users/1/relationships/roles', + 'related' => 'https://api.example.org/users/1/roles', + ], + ], + ], + ], + 'meta' => [ + 'pagination' => [ + 'page' => 1, + 'page_count' => 1, + 'page_items' => 1, + 'page_size' => 20, + 'count' => 1, + ], + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer', + ], + 'type' => [ + 'type' => 'string', + ], + 'attributes' => [ + 'type' => 'object', + ], + 'links' => [ + 'type' => 'object', + ], + 'relationships' => [ + 'type' => 'object', + ], + ], + 'required' => ['id', 'type', 'attributes'], + ], + ], + 'included' => [ + [ + 'id' => 1, + 'type' => 'roles', + 'attributes' => [ + 'name' => 'admin', + ], + ], + ], + ], + ], + 'remove extra, list of entities with included' => [ + [ + 'data' => [ + [ + 'id' => 1, + 'attributes' => [ + 'uname' => 'gustavo', + 'title' => 'gustavo supporto', + 'name' => 'gustavo', + 'surname' => 'supporto', + 'extra' => ['some' => 'thing'], + ], + 'links' => [ + 'self' => 'https://api.example.org/users/1', + ], + 'relationships' => [ + 'roles' => [ + 'links' => [ + 'self' => 'https://api.example.org/users/1/relationships/roles', + 'related' => 'https://api.example.org/users/1/roles', + ], + ], + ], + ], + ], + 'meta' => [ + 'pagination' => [ + 'page' => 1, + 'page_count' => 1, + 'page_items' => 1, + 'page_size' => 20, + 'count' => 1, + ], + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer', + ], + 'type' => [ + 'type' => 'string', + ], + 'attributes' => [ + 'type' => 'object', + ], + 'links' => [ + 'type' => 'object', + ], + 'relationships' => [ + 'type' => 'object', + ], + ], + 'required' => ['id', 'type', 'attributes'], + ], + ], + 'included' => [ + [ + 'id' => 1, + 'type' => 'roles', + 'attributes' => [ + 'name' => 'admin', + 'extra' => ['any' => 'thing'], + ], + ], + ], + 'links' => [ + 'self' => 'https://api.example.org/users', + 'first' => 'https://api.example.org/users?page=1', + 'last' => 'https://api.example.org/users?page=1', + ], + ], + ['attributes' => ['extra']], + [ + 'data' => [ + [ + 'id' => 1, + 'attributes' => [ + 'uname' => 'gustavo', + 'title' => 'gustavo supporto', + 'name' => 'gustavo', + 'surname' => 'supporto', + ], + 'links' => [ + 'self' => 'https://api.example.org/users/1', + ], + 'relationships' => [ + 'roles' => [ + 'links' => [ + 'self' => 'https://api.example.org/users/1/relationships/roles', + 'related' => 'https://api.example.org/users/1/roles', + ], + ], + ], + ], + ], + 'meta' => [ + 'pagination' => [ + 'page' => 1, + 'page_count' => 1, + 'page_items' => 1, + 'page_size' => 20, + 'count' => 1, + ], + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer', + ], + 'type' => [ + 'type' => 'string', + ], + 'attributes' => [ + 'type' => 'object', + ], + 'links' => [ + 'type' => 'object', + ], + 'relationships' => [ + 'type' => 'object', + ], + ], + 'required' => ['id', 'type', 'attributes'], + ], + ], + 'included' => [ + [ + 'id' => 1, + 'type' => 'roles', + 'attributes' => [ + 'name' => 'admin', + ], + ], + ], + 'links' => [ + 'self' => 'https://api.example.org/users', + 'first' => 'https://api.example.org/users?page=1', + 'last' => 'https://api.example.org/users?page=1', + ], + ], + ], + ]; + } + + /** + * Test `cleanResponse` attributes. + * + * @return array + * + * @return void + * @dataProvider cleanResponseAttributesProvider + * @covers ::cleanResponse() + * @covers ::removeAttributes() + */ + public function testCleanResponseAttributes(array $response, array $options, array $expected): void + { + $actual = ApiTools::cleanResponse($response, $options); + static::assertEquals($expected, $actual); + } }