Skip to content

Commit

Permalink
Changes to the returned payload to adhere to JSONAPI
Browse files Browse the repository at this point in the history
  • Loading branch information
niden committed Jul 8, 2018
1 parent 5156e4e commit 0523e71
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 127 deletions.
52 changes: 15 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ As part of the security of the API, [JWT](https://jwt.io) are used. JSON Web Tok
- Lazy loading to save resources per request
- Stop execution as early as possible when an error occurs
- Execution
- NotFound - 404 when the resource requested is not found
- Payload - Check the posted JSON string if it is correct
- Authentication - After a `/login` checks the `Authentication` header
- TokenUser - When a token is supplied, check if it corresponds to a user in the database
- NotFound - 404 when the resource requested is not found
- Payload - Check the posted JSON string if it is correct
- Authentication - After a `/login` checks the `Authentication` header
- TokenUser - When a token is supplied, check if it corresponds to a user in the database
- TokenVerification - When a token is supplied, check if it is correctly signed
- TokenValidation - When a token is supplied, check if it is valid (`issuedAt`, `notBefore`, `expires`)

Expand All @@ -45,9 +45,9 @@ The endpoints are:

`/user/get`

| Method | Payload |
|--------|-----------------------------------------------------|
| `POST` | `{"data": {"userId": 1}}` | `["token": "ab.cd.ef"]` |
| Method | Payload |
|--------|---------------------------------------------------------|
| `POST` | `{"data": {"userId": 1}}` | with Bearer Authentication` |
`/usesr/get`

Expand All @@ -63,15 +63,15 @@ The endpoints are:
"version": "1.0" // Version of the API
},
"data": [
// Payload returned
// Payload returned if successful reply (not present if there is an error)
],
"errors": {
"code": 2000, // 2000 success; 3000 error
"detail": "Error description"
"errors": [
"Error 1", // Collection of errors
"Error 2"
},
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00", // Timestamp of the response
"hash": "e6d4d57162ae0f220c8649ae50a2b79fd1cb2c60" // Hash of the timestamp and payload
"hash": "e6d4d57162ae0f220c8649ae50a2b79fd1cb2c60" // Hash of the timestamp and payload (`data` if success, `error` if failure)
}
}
```
Expand All @@ -81,10 +81,8 @@ The endpoints are:
"jsonapi": {
"version": "1.0"
},
"data": [],
"errors": {
"code": 3000,
"detail": "404 Not Found"
"404 Not Found"
},
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00",
Expand All @@ -99,10 +97,8 @@ The endpoints are:
"jsonapi": {
"version": "1.0"
},
"data": [],
"errors": {
"code": 3000,
"detail": "Description of the error"
"Description of the error"
},
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00",
Expand All @@ -120,10 +116,6 @@ The endpoints are:
"data": [
// Data returned
],
"errors": {
"code": 2000,
"detail": ""
},
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00",
"hash": "e6d4d57162ae0f220c8649ae50a2b79fd1cb2c60"
Expand All @@ -140,10 +132,6 @@ The endpoints are:
"data": {
"token": "ab.cd.ef"
},
"errors": {
"code": 2000,
"detail": ""
},
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00",
"hash": "e6d4d57162ae0f220c8649ae50a2b79fd1cb2c60"
Expand All @@ -167,10 +155,6 @@ The endpoints are:
"tokenId": "99009900"
}
],
"errors": {
"code": 2000,
"detail": ""
},
"meta": {
"timestamp": "2018-06-08T17:05:14+00:00",
"hash": "344d9766003e14409ab08df863d37d1ef44e5b60"
Expand Down Expand Up @@ -201,10 +185,6 @@ The endpoints are:
"tokenId": "99009900"
}
],
"errors": {
"code": 2000,
"detail": ""
},
"meta": {
"timestamp": "2018-06-08T15:07:35+00:00",
"hash": "6219ae83afaebc08da4250c4fd23ea1b4843d"
Expand All @@ -213,8 +193,6 @@ The endpoints are:
```

### TODO
- Remove `/login` endpoint. Leave the generation of the JWT to the consumer
- Remove `/login` endpoint. Leave the generation of the JWT to the consumer (maybe)
- Perhaps add a new claim to the token tied to the device? `setClaim('deviceId', 'Web-Server')`. This will allow the client application to invalidate access to a device that has already been logged in.
- Write examples of code to send to the client


37 changes: 11 additions & 26 deletions library/Http/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,10 @@

class Response extends PhResponse
{
/** @var int */
const STATUS_SUCCESS = 2000;

/** @var int */
const STATUS_ERROR = 3000;

/** @var array */
protected $data = [
'code' => self::STATUS_SUCCESS,
'detail' => '',
];

/**
* @var array
*/
private $content = [];
/**
* Sets the payload code as Error
*
Expand All @@ -32,10 +24,7 @@ class Response extends PhResponse
*/
public function setPayloadError(string $detail = ''): Response
{
$this->data = [
'code' => self::STATUS_ERROR,
'detail' => $detail,
];
$this->content['errors'][] = $detail;
$this->setPayloadContent();

return $this;
Expand All @@ -50,8 +39,10 @@ public function setPayloadError(string $detail = ''): Response
*/
public function setPayloadSuccess($content = []): Response
{
$this->data['code'] = self::STATUS_SUCCESS;
$this->setPayloadContent($content);
$data = (true === is_array($content)) ? $content : [$content];

$this->content['data'] = $data;
$this->setPayloadContent();

return $this;
}
Expand All @@ -61,16 +52,10 @@ public function setPayloadSuccess($content = []): Response
* setJsonContent, so as to provide a uniformed payload no matter what
* the response is
*
* @param null|string|array $content The content
*
* @return Response
*/
public function setPayloadContent($content = []): Response
public function setPayloadContent(): Response
{
$data = (true === is_array($content)) ? $content : [$content];

$this->data['data'] = $data;

parent::setJsonContent($this->processPayload());

$this->setStatusCode(200);
Expand All @@ -88,7 +73,7 @@ public function setPayloadContent($content = []): Response
private function processPayload(): array
{
$manager = new Manager();
$resource = new Item($this->data, new PayloadTransformer());
$resource = new Item($this->content, new PayloadTransformer());
$data = $manager->createData($resource)->toArray();

return $data['data'];
Expand Down
2 changes: 1 addition & 1 deletion library/Traits/ResponseTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function process(Micro $api)
/** @var Response $response */
$response = $api->getService('response');
$data = $api->getReturnedValue();
$response->setPayloadContent($data);
$response->setPayloadSuccess($data);

if (true !== $response->isSent()) {
$response->send();
Expand Down
17 changes: 6 additions & 11 deletions library/Transformers/PayloadTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,19 @@ class PayloadTransformer extends TransformerAbstract
*/
public function transform(array $content)
{
$payload = $content['data'];
$code = $content['code'];
$detail = $content['detail'];
$section = (true === isset($content['errors'])) ? 'errors' : 'data';
$timestamp = date('c');

return [
$result = [
'jsonapi' => [
'version' => '1.0',
],
'data' => $payload,
'errors' => [
'code' => $code,
'detail' => $detail,
],
$section => $content[$section],
'meta' => [
'timestamp' => $timestamp,
'hash' => sha1($timestamp . json_encode($payload)),
'hash' => sha1($timestamp . json_encode($content[$section])),
],
];

return $result;
}
}
24 changes: 7 additions & 17 deletions tests/_support/ApiTester.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,8 @@ public function seeResponseIsSuccessful()
'jsonapi' => [
'version' => 'string'
],
'data' => 'array',
'errors' => [
'code' => 'integer',
'detail' => 'string',
],
// 'data' => 'array',
// 'errors' => 'array',
'meta' => [
'timestamp' => 'string:date',
'hash' => 'string',
Expand All @@ -50,10 +47,11 @@ public function seeResponseIsSuccessful()

$response = $this->grabResponse();
$response = json_decode($response, true);
$data = json_encode($response['data']);
$errors = $response['errors'] ?? [];
$section = (count($errors) > 0) ? 'errors' : 'data';
$timestamp = $response['meta']['timestamp'];
$hash = $response['meta']['hash'];
$this->assertEquals($hash, sha1($timestamp . $data));
$this->assertEquals($hash, sha1($timestamp . json_encode($response[$section])));
}

public function seeErrorJsonResponse(string $message)
Expand All @@ -64,8 +62,7 @@ public function seeErrorJsonResponse(string $message)
'version' => '1.0',
],
'errors' => [
'code' => Response::STATUS_ERROR,
'detail' => $message,
$message,
],
]
);
Expand All @@ -77,16 +74,9 @@ public function seeSuccessJsonResponse(array $data = [])
'jsonapi' => [
'version' => '1.0',
],
'errors' => [
'code' => Response::STATUS_SUCCESS,
'detail' => '',
],
'data' => $data,
];

if (true !== empty($da)) {
$contents['data'] = $data;
}

$this->seeResponseContainsJson($contents);
}

Expand Down
7 changes: 6 additions & 1 deletion tests/api/LoginCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Niden\Tests\api;

use ApiTester;
use function json_decode;
use Niden\Exception\Exception;
use Niden\Http\Response;
use Niden\Models\Users;
Expand Down Expand Up @@ -57,6 +58,10 @@ public function loginKnownUser(ApiTester $I)

$I->sendPOST(Data::$loginUrl, Data::loginJson());
$I->seeResponseIsSuccessful();
$I->seeSuccessJsonResponse();
$response = $I->grabResponse();
$data = json_decode($response, true);
$I->assertTrue(isset($data['data']));
$I->assertTrue(isset($data['data']['token']));
$I->assertTrue(isset($data['meta']));
}
}
5 changes: 0 additions & 5 deletions tests/api/Users/UserCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ public function loginKnownUserGetUnknownUser(ApiTester $I)
$I->deleteHeader('Authorization');
$I->sendPOST(Data::$loginUrl, Data::loginJson());
$I->seeResponseIsSuccessful();
$I->seeSuccessJsonResponse();

$response = $I->grabResponse();
$response = json_decode($response, true);
Expand All @@ -46,7 +45,6 @@ public function loginKnownUserIncorrectSignature(ApiTester $I)
$I->deleteHeader('Authorization');
$I->sendPOST(Data::$loginUrl, Data::loginJson());
$I->seeResponseIsSuccessful();
$I->seeSuccessJsonResponse();

$signer = new Sha512();
$builder = new Builder();
Expand Down Expand Up @@ -75,7 +73,6 @@ public function loginKnownUserExpiredToken(ApiTester $I)
$I->deleteHeader('Authorization');
$I->sendPOST(Data::$loginUrl, Data::loginJson());
$I->seeResponseIsSuccessful();
$I->seeSuccessJsonResponse();

$signer = new Sha512();
$builder = new Builder();
Expand Down Expand Up @@ -104,7 +101,6 @@ public function loginKnownUserInvalidToken(ApiTester $I)
$I->deleteHeader('Authorization');
$I->sendPOST(Data::$loginUrl, Data::loginJson());
$I->seeResponseIsSuccessful();
$I->seeSuccessJsonResponse();

$signer = new Sha512();
$builder = new Builder();
Expand Down Expand Up @@ -133,7 +129,6 @@ public function loginKnownUserInvalidUserInToken(ApiTester $I)
$I->deleteHeader('Authorization');
$I->sendPOST(Data::$loginUrl, Data::loginJson());
$I->seeResponseIsSuccessful();
$I->seeSuccessJsonResponse();

$signer = new Sha512();
$builder = new Builder();
Expand Down
1 change: 0 additions & 1 deletion tests/api/Users/UsersCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,5 @@ public function getManyUsersWithNoData(ApiTester $I)
$I->sendPOST(Data::$usersGetUrl);
$I->deleteHeader('Authorization');
$I->seeResponseIsSuccessful();
$I->seeSuccessJsonResponse();
}
}
3 changes: 1 addition & 2 deletions tests/unit/BootstrapCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public function checkBootstrap(CliTester $I)
$results = json_decode($actual, true);
$I->assertEquals('1.0', $results['jsonapi']['version']);
$I->assertEmpty($results['data']);
$I->assertEquals(3000, $results['errors']['code']);
$I->assertEquals('404 Not Found', $results['errors']['detail']);
$I->assertEquals('404 Not Found', $results['errors'][0]);
}
}
Loading

0 comments on commit 0523e71

Please sign in to comment.