Skip to content

Commit

Permalink
Merge pull request #2 from niden/master
Browse files Browse the repository at this point in the history
v1.1
  • Loading branch information
niden committed Aug 3, 2018
2 parents 9f25cec + 947e0d1 commit 3deca17
Show file tree
Hide file tree
Showing 112 changed files with 5,709 additions and 1,062 deletions.
212 changes: 96 additions & 116 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# phalcon-api
Sample API using Phalcon

[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/niden/phalcon-api/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/niden/phalcon-api/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/niden/phalcon-api/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/niden/phalcon-api/?branch=master)
[![Build Status](https://scrutinizer-ci.com/g/niden/phalcon-api/badges/build.png?b=master)](https://scrutinizer-ci.com/g/niden/phalcon-api/build-status/master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/phalcon/phalcon-api/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/phalcon/phalcon-api/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/phalcon/phalcon-api/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/phalcon/phalcon-api/?branch=master)
[![Build Status](https://scrutinizer-ci.com/g/phalcon/phalcon-api/badges/build.png?b=master)](https://scrutinizer-ci.com/g/phalcon/phalcon-api/build-status/master)


Implementation of an API application using the Phalcon Framework (https://phalconphp.com)
Implementation of an API application using the Phalcon Framework [https://phalconphp.com](https://phalconphp.com)

### Installation
- Clone the project
- In the project folder run `nanobox run php-server`
- Hit the IP address with postman

This requires [nanobox](https://nanobox.io) to be present in your system. Visit their site for installation instructions.
**NOTE** This requires [nanobox](https://nanobox.io) to be present in your system. Visit their site for installation instructions.

### Features
##### JWT Tokens
Expand All @@ -24,65 +24,106 @@ As part of the security of the API, [JWT](https://jwt.io) are used. JSON Web Tok
- 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
- 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`)

##### JSONAPI
This implementation follows the [JSON API](https://jsonapi.org) standard. All responses are formatted according to the standard, which offers a uniformed way of presenting data, simple or compound documents, includes (related data), sparse fieldsets, sorting, patination and filtering.

### Usage

#### Requests
All requests to the API have be submitted using `POST`. All requests must send a JSON string with one root element `data`. Data needed for the request must be under the `data` element

The endpoints are:

`/login`

| Method | Payload |
|--------|--------------------------------------------------------|
| `POST` | `{"data": {"username": "niden", "password": "12345"}}` |
The routes available are:

| Method | Route | Parameters | Action |
|--------|--------------------|------------------------------------|----------------------------------------------------------|
| `POST` | `login` | `username`, `password` | Login - get Token |
| `POST` | `companies` | `name`, `address`, `city`, `phone` | Add a company record in the database |
| `GET` | `companies` | | Get companies. Empty resultset if no data present |
| `GET` | `companies` | Numeric Id | Get company by id. 404 if record does not exist |
| `GET` | `individuals` | | Get individuals. Empty resultset if no data present |
| `GET` | `individuals` | Numeric Id | Get individual by id. 404 if record does not exist |
| `GET` | `individual-types` | | Get individual types. Empty resultset if no data present |
| `GET` | `individual-types` | Numeric Id | Get individual type by id. 404 if record does not exist |
| `GET` | `products` | | Get products. Empty resultset if no data present |
| `GET` | `products` | Numeric Id | Get product by id. 404 if record does not exist |
| `GET` | `product-types` | | Get product types. Empty resultset if no data present |
| `GET` | `product-types` | Numeric Id | Get product type by id. 404 if record does not exist |
| `GET` | `users` | | Get users. Empty resultset if no data present |
| `GET` | `users` | Numeric Id | Get user by id. 404 if record does not exist |
#### Relationships

`/companies/<number>/individuals`
`/companies/<number>/products`
`/companies/<number>/individuals,products`

`/companies/<number>/relationships/individuals`
`/companies/<number>/relationships/products`
`/companies/<number>/relationships/individuals,products`

`individuals/<number>/companies`
`individuals/<number>/individual-types`
`individuals/<number>/companies,individual-types`

`individuals/<number>/relationships/companies`
`individuals/<number>/relationships/individual-types`
`individuals/<number>/relationships/companies,individual-types`

`individual-types/<number>/individuals`
`individual-types/<number>/relationships/individuals`

`products/<number>/companies`
`products/<number>/product-types`
`products/<number>/companies,product-types`

`products/<number>/relationships/companies`
`products/<number>/relationships/product-types`
`products/<number>/relationships/companies,product-types`

`product-types/<number>/products`
`product-types/<number>/relationships/products`


`/user/get`

| Method | Payload |
|--------|---------------------------------------------------------|
| `POST` | `{"data": {"userId": 1}}` | with Bearer Authentication` |
`/usesr/get`

| Method | Payload |
|--------|---------|
| `POST` | Empty |
#### Responses
##### Structure
**Top Elements**
- `jsonapi` Contains the `version` of the API as a sub element
- `data` Data returned. Is not present if the `errors` is present
- `errors` Collection of errors that occurred in this request. Is not present if the `data` is present
- `meta` Contains `timestamp` and `hash` of the `json_encode($data)` or `json_encode($errors)`

After a `GET` the API will always return a collection of records, even if there is only one returned. If no data is found, an empty resultset will be returned.

Each endpoint returns records that follow this structure:
```json
{
"jsonapi": {
"version": "1.0" // Version of the API
},
"data": [
// Payload returned if successful reply (not present if there is an error)
],
"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 (`data` if success, `error` if failure)
"id": 1051,
"type": "users",
"attributes": {
"status": 1,
"username": "niden",
"issuer": "https:\/\/niden.net",
"tokenPassword": "11110000",
"tokenId": "11001100"
}
}
```
##### 404

The record always has `id` and `type` present at the top level. `id` is the unique id of the record in the database. `type` is a string representation of what the object is. In the above example it is a `users` record. Additional data from each record are under the `attributes` node.

#### Samples
**404**
```json
{
"jsonapi": {
"version": "1.0"
},
"errors": {
"404 Not Found"
"404 not found"
},
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00",
Expand All @@ -91,14 +132,16 @@ The endpoints are:
}
```

##### Error
**Error**

```json
{
"jsonapi": {
"version": "1.0"
},
"errors": {
"Description of the error"
"Description of the error no 1",
"Description of the error no 2"
},
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00",
Expand All @@ -107,92 +150,29 @@ The endpoints are:
}
```

##### Success
##### Success
```json
{
"jsonapi": {
"version": "1.0"
},
"data": [
// Data returned
],
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00",
"hash": "e6d4d57162ae0f220c8649ae50a2b79fd1cb2c60"
}
}
```

`/login`
```json
{
"jsonapi": {
"version": "1.0"
},
"data": {
"token": "ab.cd.ef"
},
"meta": {
"timestamp": "2018-06-08T15:04:34+00:00",
"hash": "e6d4d57162ae0f220c8649ae50a2b79fd1cb2c60"
}
}
```

`/user/get`
```json
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"id": 1244,
"status": 1,
"username": "phalcon",
"issuer": "https:\/\/phalconphp.com",
"tokenPassword": "00001111",
"tokenId": "99009900"
}
],
"meta": {
"timestamp": "2018-06-08T17:05:14+00:00",
"hash": "344d9766003e14409ab08df863d37d1ef44e5b60"
}
}
```
`/users/get`
```json
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"id": 1051,
"status": 1,
"username": "niden",
"issuer": "https:\/\/niden.net",
"tokenPassword": "11110000",
"tokenId": "11001100"
},
{
"id": 1244,
"status": 1,
"username": "phalcon",
"issuer": "https:\/\/phalconphp.com",
"tokenPassword": "00001111",
"tokenId": "99009900"
}
],
"meta": {
"timestamp": "2018-06-08T15:07:35+00:00",
"hash": "6219ae83afaebc08da4250c4fd23ea1b4843d"
}
}
```

For more information regarding responses, please check [JSON API](https://jsonapi.org)

### TODO
- 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.
- ~~Work on companies GET~~
- ~~Work on relationships and data returned~~
- Write examples of code to send to the client
- Create docs endpoint
- Work on pagination
- Work on filters
- Work on sorting
- 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.
2 changes: 2 additions & 0 deletions api/config/providers.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Enabled providers. Order does matter
*/

use Niden\Providers\CacheDataProvider;
use Niden\Providers\ConfigProvider;
use Niden\Providers\DatabaseProvider;
use Niden\Providers\ErrorHandlerProvider;
Expand All @@ -22,4 +23,5 @@
RequestProvider::class,
ResponseProvider::class,
RouterProvider::class,
CacheDataProvider::class,
];
Loading

0 comments on commit 3deca17

Please sign in to comment.