spec: Revisit spacing and indentation.
Co-authored-by: pancho horrillo <pedrofelipe.horrillo@bbva.com>
This commit is contained in:
+131
-189
@@ -1,5 +1,6 @@
|
|||||||
# Kapow!
|
# Kapow!
|
||||||
|
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
|
|
||||||
Because we think that:
|
Because we think that:
|
||||||
@@ -105,14 +106,15 @@ Kapow! server interacts with the outside world only through its HTTP API. Any
|
|||||||
program making the correct HTTP request to a Kapow! server, can change its
|
program making the correct HTTP request to a Kapow! server, can change its
|
||||||
behavior.
|
behavior.
|
||||||
|
|
||||||
|
|
||||||
## Design Principles
|
## Design Principles
|
||||||
|
|
||||||
* All requests and responses will leverage JSON as the data encoding method.
|
* All requests and responses will leverage JSON as the data encoding method.
|
||||||
|
|
||||||
* The API calls responses will have two distinct parts:
|
* The API calls responses will have two distinct parts:
|
||||||
* The HTTP status code (e.g., 400, Bad Request). The target audience of this
|
* The HTTP status code (e.g., `400`, which is a bad request). The target
|
||||||
information is the client code. The client can thus use this information to
|
audience of this information is the client code. The client can thus use
|
||||||
control the program flow.
|
this information to control the program flow.
|
||||||
* The JSON-encoded message. The target audience in this case is the human
|
* The JSON-encoded message. The target audience in this case is the human
|
||||||
operating the client. The human can use this information to make a
|
operating the client. The human can use this information to make a
|
||||||
decision on how to proceed.
|
decision on how to proceed.
|
||||||
@@ -126,59 +128,42 @@ Let's illustrate these ideas with an example: TODO
|
|||||||
FIXME: consider what to do when deleting objects. Isn't it too much to
|
FIXME: consider what to do when deleting objects. Isn't it too much to
|
||||||
return the list of all deleted objects in such a request?
|
return the list of all deleted objects in such a request?
|
||||||
|
|
||||||
## API elements
|
|
||||||
|
## API Elements
|
||||||
|
|
||||||
|
|
||||||
### Servers
|
### Servers
|
||||||
|
|
||||||
TODO: Define servers' API
|
TODO: Define servers' API
|
||||||
|
|
||||||
|
|
||||||
### Routes
|
### Routes
|
||||||
|
|
||||||
Routes are the mechanism that allows Kapow! to find the correct program to
|
Routes are the mechanism that allows Kapow! to find the correct program to
|
||||||
respond to an external event (e.g. an incomming HTTP request).
|
respond to an external event (e.g. an incomming HTTP request).
|
||||||
|
|
||||||
|
|
||||||
#### List routes
|
#### List routes
|
||||||
|
|
||||||
Returns JSON data about the current routes.
|
Returns JSON data about the current routes.
|
||||||
|
|
||||||
* **URL**
|
* **URL**: `/routes`
|
||||||
|
* **Method**: `GET`
|
||||||
`/routes`
|
* **Success Response**:
|
||||||
|
|
||||||
* **Method**
|
|
||||||
|
|
||||||
`GET`
|
|
||||||
|
|
||||||
* **Success Response**
|
|
||||||
|
|
||||||
* **Code**: `200 OK`<br />
|
* **Code**: `200 OK`<br />
|
||||||
**Content**: TODO
|
**Content**: TODO
|
||||||
|
* **Sample Call**: TODO
|
||||||
|
* **Notes**: Currently all routes are returned; in the future, a filter may be accepted.
|
||||||
|
|
||||||
* **Sample Call**
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
* **Notes**
|
|
||||||
|
|
||||||
Currently all routes are returned; in the future, a filter may be accepted.
|
|
||||||
|
|
||||||
#### Append route
|
#### Append route
|
||||||
|
|
||||||
Accepts JSON data that defines a new route to be appended to the current routes.
|
Accepts JSON data that defines a new route to be appended to the current routes.
|
||||||
|
|
||||||
* **URL**
|
|
||||||
|
|
||||||
`/routes`
|
|
||||||
|
|
||||||
* **Method**
|
|
||||||
|
|
||||||
`POST`
|
|
||||||
|
|
||||||
|
* **URL**: `/routes`
|
||||||
|
* **Method**: `POST`
|
||||||
* **Header**: `Content-Type: application/json`
|
* **Header**: `Content-Type: application/json`
|
||||||
|
* **Data Params**:<br />
|
||||||
* **Data Params**
|
|
||||||
|
|
||||||
* **Content**:
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -187,58 +172,41 @@ Returns JSON data about the current routes.
|
|||||||
"command": "echo Hello World | response /body"
|
"command": "echo Hello World | response /body"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* **Success Responses**:
|
||||||
* **Success Response**
|
|
||||||
|
|
||||||
* **Code**: `200 OK`<br />
|
* **Code**: `200 OK`<br />
|
||||||
**Header**: `Content-Type: application/json`<br />
|
**Header**: `Content-Type: application/json`<br />
|
||||||
**Content**:
|
**Content**:<br />
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url_pattern": "/hello",
|
"url_pattern": "/hello",
|
||||||
"entrypoint": null,
|
"entrypoint": null,
|
||||||
"command": "echo Hello World | response /body",
|
"command": "echo Hello World | response /body",
|
||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* **Error Responses**:
|
||||||
* **Error Response**
|
|
||||||
|
|
||||||
* **Code**: `400 Bad Request`<br />
|
* **Code**: `400 Bad Request`<br />
|
||||||
**Header**: `Content-Type: application/json`<br />
|
**Header**: `Content-Type: application/json`<br />
|
||||||
**Content**: `{ "error": "Malformed JSON." }`
|
**Content**: `{ "error": "Malformed JSON." }`
|
||||||
|
|
||||||
* **Code**: `400 Bad Request`<br />
|
* **Code**: `400 Bad Request`<br />
|
||||||
**Header**: `Content-Type: application/json`<br />
|
**Header**: `Content-Type: application/json`<br />
|
||||||
**Content**: `{ "error": "Mandatory field(s) not provided." }`
|
**Content**: `{ "error": "Mandatory field(s) not provided." }`
|
||||||
|
* **Sample Call**: TODO
|
||||||
* **Sample Call**
|
* **Notes**:
|
||||||
TODO
|
|
||||||
|
|
||||||
* **Notes**
|
|
||||||
|
|
||||||
* A successful request will yield a response containing all the effective
|
* A successful request will yield a response containing all the effective
|
||||||
parameters that were applied.
|
parameters that were applied.
|
||||||
|
|
||||||
|
|
||||||
#### Insert a route
|
#### Insert a route
|
||||||
|
|
||||||
Accepts JSON data that defines a new route to be inserted at the specified
|
Accepts JSON data that defines a new route to be inserted at the specified
|
||||||
index to the current routes.
|
index to the current routes.
|
||||||
|
|
||||||
* **URL**
|
* **URL**: `/routes`
|
||||||
|
* **Method**: `PUT`
|
||||||
`/routes`
|
|
||||||
|
|
||||||
* **Method**
|
|
||||||
|
|
||||||
`PUT`
|
|
||||||
|
|
||||||
* **Header**: `Content-Type: application/json`
|
* **Header**: `Content-Type: application/json`
|
||||||
|
* **Data Params**:<br />
|
||||||
* **Data Params**
|
|
||||||
|
|
||||||
* **Content**:
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -247,73 +215,52 @@ TODO
|
|||||||
"command": "echo Hello World | response /body",
|
"command": "echo Hello World | response /body",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* **Success Responses**:
|
||||||
* **Success Responses**
|
|
||||||
|
|
||||||
* **Code**: `200 OK`<br />
|
* **Code**: `200 OK`<br />
|
||||||
**Header**: `Content-Type: application/json`<br />
|
**Header**: `Content-Type: application/json`<br />
|
||||||
**Content**:
|
**Content**:<br />
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url_pattern": "/hello",
|
"url_pattern": "/hello",
|
||||||
"entrypoint": null,
|
"entrypoint": null,
|
||||||
"command": "echo Hello World | response /body",
|
"command": "echo Hello World | response /body",
|
||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* **Error Responses**:
|
||||||
* **Error Responses**
|
|
||||||
|
|
||||||
* **Code**: `400 Bad Request`<br />
|
* **Code**: `400 Bad Request`<br />
|
||||||
**Header**: `Content-Type: application/json`<br />
|
**Header**: `Content-Type: application/json`<br />
|
||||||
**Content**: `{ "error": "Malformed JSON." }`
|
**Content**: `{ "error": "Malformed JSON." }`
|
||||||
|
|
||||||
* **Code**: `400 Bad Request`<br />
|
* **Code**: `400 Bad Request`<br />
|
||||||
**Header**: `Content-Type: application/json`<br />
|
**Header**: `Content-Type: application/json`<br />
|
||||||
**Content**: `{ "error": "Mandatory field(s) not provided." }`
|
**Content**: `{ "error": "Mandatory field(s) not provided." }`
|
||||||
|
* **Sample Call**: TODO
|
||||||
* **Sample Call**
|
* **Notes**:
|
||||||
TODO
|
|
||||||
|
|
||||||
* **Notes**
|
|
||||||
|
|
||||||
* Route numbering starts at zero.
|
* Route numbering starts at zero.
|
||||||
* When `index` is not provided or is less than 0 the route will be inserted
|
* When `index` is not provided or is less than 0 the route will be inserted
|
||||||
first, effectively making it index 0.
|
first, effectively making it index 0.
|
||||||
* Conversely when `index` is greater than the number of entries on the route
|
* Conversely, when `index` is greater than the number of entries on the route
|
||||||
table it will be inserted last.
|
table, it will be inserted last.
|
||||||
* A successful request will yield a response containing all the effective
|
* A successful request will yield a response containing all the effective
|
||||||
parameters that were applied.
|
parameters that were applied.
|
||||||
|
|
||||||
|
|
||||||
#### Delete a route
|
#### Delete a route
|
||||||
|
|
||||||
Removes the route identified by `:id`.
|
Removes the route identified by `:id`.
|
||||||
|
|
||||||
* **URL**
|
* **URL**: `/routes/:id`
|
||||||
|
* **Method**: `DELETE`
|
||||||
`/routes/:id`
|
* **Success Response**:
|
||||||
|
|
||||||
* **Method**
|
|
||||||
|
|
||||||
`DELETE`
|
|
||||||
|
|
||||||
* **Success Response**
|
|
||||||
|
|
||||||
* **Code**: `200 OK`<br />
|
* **Code**: `200 OK`<br />
|
||||||
**Content**: TODO
|
**Content**: TODO
|
||||||
|
* **Error Response**:
|
||||||
* **Error Response**
|
|
||||||
|
|
||||||
* **Code**: `404 Not Found`<br />
|
* **Code**: `404 Not Found`<br />
|
||||||
**Header**: `Content-Type: application/json`<br />
|
**Header**: `Content-Type: application/json`<br />
|
||||||
**Content**: `{ "error": "Unknown route", "route_id": "{{ :id }}" }`
|
**Content**: `{ "error": "Unknown route", "route_id": "{{ :id }}" }`
|
||||||
|
* **Sample Call**: TODO
|
||||||
|
* **Notes**:
|
||||||
* **Sample Call**
|
|
||||||
TODO
|
|
||||||
|
|
||||||
* **Notes**
|
|
||||||
|
|
||||||
|
|
||||||
### Handlers
|
### Handlers
|
||||||
@@ -350,31 +297,29 @@ following keys:
|
|||||||
└──── <entry>
|
└──── <entry>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Example Keys
|
#### Example Keys
|
||||||
|
|
||||||
1. Read the request URL path.
|
- Read the request URL path.
|
||||||
- Scenario: Request URL is `http://localhost:8080/example?q=foo&r=bar`
|
- Scenario: Request URL is `http://localhost:8080/example?q=foo&r=bar`
|
||||||
- Key: `/request/path`
|
- Key: `/request/path`
|
||||||
- Access: Read-Only
|
- Access: Read-Only
|
||||||
- Returned Value: `/example?q=foo&r=bar`
|
- Returned Value: `/example?q=foo&r=bar`
|
||||||
- Comment: That would provide read-only access to the request URL path.
|
- Comment: That would provide read-only access to the request URL path.
|
||||||
|
- Read an specific URL parameter.
|
||||||
2. Read an specific URL parameter.
|
- Scenario: Request URL is `http://localhost:8080/example?q=foo&r=bar`
|
||||||
Scenario: Request URL is `http://localhost:8080/example?q=foo&r=bar`
|
- Key: `/request/params/q`
|
||||||
Key: `/request/params/q`
|
- Access: Read-Only
|
||||||
Access: Read-Only
|
- Returned Value: `foo`
|
||||||
Returned Value: `foo`
|
- Comment: That would provide read-only access to the request URL parameter `q`.
|
||||||
Comment: That would provide read-only access to the request URL parameter `q`.
|
- Obtain the `Content-Type` header of the request.
|
||||||
|
- Scenario: A POST request with a JSON body and the header `Content-Type` set to `application/json`.
|
||||||
3. Obtain the `Content-Type` header of the request.
|
- Key: `/request/headers/Content-Type`
|
||||||
Scenario: A POST request with a JSON body and the header `Content-Type` set to `application/json`.
|
- Access: Read-Only
|
||||||
Key: `/request/headers/Content-Type`
|
- Returned Value: `application/json`
|
||||||
Access: Read-Only
|
- Comment: That would provide read-only access to the value of the request header `Content-Type`.
|
||||||
Returned Value: `application/json`
|
- Read a field from a form.
|
||||||
Comment: That would provide read-only access to the value of the request header `Content-Type`.
|
- Scenario: A request generated by submitting this form:<br />
|
||||||
|
|
||||||
4. Read a field from a form.
|
|
||||||
Scenario: A request generated by submitting this form:
|
|
||||||
```
|
```
|
||||||
<form method="post">
|
<form method="post">
|
||||||
First name:<br>
|
First name:<br>
|
||||||
@@ -384,84 +329,67 @@ following keys:
|
|||||||
<input type="submit" value="Submit">
|
<input type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
Key: `/request/form/firstname`
|
- Key: `/request/form/firstname`
|
||||||
Access: Read-Only
|
- Access: Read-Only
|
||||||
Returned Value: `Jane`
|
- Returned Value: `Jane`
|
||||||
Comment: That would provide read-only access to the value of the field `firstname` of the form.
|
- Comment: That would provide read-only access to the value of the field `firstname` of the form.
|
||||||
|
- Set the response status code.
|
||||||
5. Set the response status code.
|
- Scenario: A request is being attended.
|
||||||
Scenario: A request is being attended.
|
- Key: `/response/status`
|
||||||
Key: `/response/status`
|
- Access: Write-Only
|
||||||
Access: Write-Only
|
- Acceptable Value: A 3-digit integer. Must match `[0-9]{3}`.
|
||||||
Acceptable Value: A 3-digit integer. Must match `[0-9]{3}`.
|
- Default Value: `200`
|
||||||
Default Value: 200
|
- Comment: It is customary to use the HTTP status code as defined at [RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1).
|
||||||
Comment: It is customary to use the HTTP status code as defined at [https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1](RFC2616).
|
- Set the response body.
|
||||||
|
- Scenario: A request is being attended.
|
||||||
6. Set the response body.
|
- Key: `/response/body`
|
||||||
Scenario: A request is being attended.
|
- Access: Write-Only
|
||||||
Key: `/response/body`
|
- Acceptable Value: Any string of bytes.
|
||||||
Access: Write-Only
|
- Default Value: N/A
|
||||||
Acceptable Value: Any string of bytes.
|
- Comment: For media types other than `application/octet-stream` you should specify the appropiate `Content-Type` header.
|
||||||
Default Value: N/A
|
|
||||||
Comment: For media types other than `application/octet-stream` you should
|
|
||||||
specify the appropiate `Content-Type` header.
|
|
||||||
|
|
||||||
|
|
||||||
**Note**: Parameters under `request` are read-only and, conversely, parameters under
|
**Note**: Parameters under `request` are read-only and, conversely, parameters under
|
||||||
`response` are write-only.
|
`response` are write-only.
|
||||||
|
|
||||||
|
|
||||||
#### Get handler key
|
#### Get handler key
|
||||||
|
|
||||||
Returns the value of the requested key, or an error if the key doesn't exist or is invalid.
|
Returns the value of the requested key, or an error if the key doesn't exist or is invalid.
|
||||||
|
|
||||||
* **URL**
|
|
||||||
|
|
||||||
`/handlers/{:handler_id}{:key}`
|
|
||||||
|
|
||||||
* **Method**
|
|
||||||
|
|
||||||
`GET`
|
|
||||||
|
|
||||||
* **URL Params**
|
|
||||||
|
|
||||||
FIXME: We think that here should be options to cook the value in some way, or get it raw.
|
|
||||||
|
|
||||||
* **Success Responses**
|
|
||||||
|
|
||||||
|
* **URL**: `/handlers/{:handler_id}{:key}`
|
||||||
|
* **Method**: `GET`
|
||||||
|
* **URL Params**: FIXME: We think that here should be options to cook the value in some way, or get it raw.
|
||||||
|
* **Success Responses**:
|
||||||
* **Code**: `200 OK`<br />
|
* **Code**: `200 OK`<br />
|
||||||
**Header**: `Content-Type: application/octet-stream`<br />
|
**Header**: `Content-Type: application/octet-stream`<br />
|
||||||
**Content**: The value for that key. Note that it may be empty.
|
**Content**: The value for that key. Note that it may be empty.
|
||||||
|
* **Error Responses**:
|
||||||
* **Error Responses**
|
* Key is invalid.<br />
|
||||||
|
|
||||||
* Key is invalid.
|
|
||||||
**Code**: `400 Bad Request`<br />
|
**Code**: `400 Bad Request`<br />
|
||||||
**Content**: None.<br />
|
**Content**: None.<br />
|
||||||
**Notes**: Check the list of valid keys at the top of this section.
|
**Notes**: Check the list of valid keys at the top of this section.
|
||||||
|
* Entry not found.<br />
|
||||||
* Entry not found.
|
|
||||||
**Code**: `404 Not Found`<br />
|
**Code**: `404 Not Found`<br />
|
||||||
**Content**: None.<br />
|
**Content**: None.<br />
|
||||||
|
* **Sample Call**: TODO
|
||||||
* **Sample Call**
|
* **Notes**: TODO
|
||||||
TODO
|
|
||||||
|
|
||||||
* **Notes**
|
|
||||||
|
|
||||||
|
|
||||||
#### Overwrite the value for a handler key
|
#### Overwrite the value for a handler key
|
||||||
* **URL**
|
|
||||||
* **Method**
|
* **URL**:
|
||||||
POST
|
* **Method**: `POST`
|
||||||
* **URL Params**
|
* **URL Params**:
|
||||||
* **Data Params**
|
* **Data Params**:
|
||||||
* **Success Response**
|
* **Success Response**:
|
||||||
* **Error Response**
|
* **Error Response**:
|
||||||
* **Sample Call**
|
* **Sample Call**:
|
||||||
* **Notes**
|
* **Notes**:
|
||||||
|
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
|
|
||||||
## Test Suite Notes
|
## Test Suite Notes
|
||||||
|
|
||||||
The test suite is located on [blebleble] directory.
|
The test suite is located on [blebleble] directory.
|
||||||
@@ -469,32 +397,46 @@ You can run it by ...
|
|||||||
|
|
||||||
|
|
||||||
# Framework
|
# Framework
|
||||||
|
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
Any compliant implementation of Kapow! must provide these commands:
|
Any compliant implementation of Kapow! must provide these commands:
|
||||||
|
|
||||||
|
|
||||||
### `kapow`
|
### `kapow`
|
||||||
|
|
||||||
This implements the server, yaddayadda
|
This implements the server, yaddayadda
|
||||||
|
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
|
|
||||||
### `kroute`
|
### `kroute`
|
||||||
|
|
||||||
TODISCUSS: maybe consider using `kapow route` instead
|
TODISCUSS: maybe consider using `kapow route` instead
|
||||||
|
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
|
|
||||||
### `request`
|
### `request`
|
||||||
|
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
|
|
||||||
### `response`
|
### `response`
|
||||||
|
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
## Full-fledged example (TODO: express it more simply)
|
|
||||||
|
## An End-to-End Example
|
||||||
|
|
||||||
|
|
||||||
## Test Suite Notes
|
## Test Suite Notes
|
||||||
|
|
||||||
|
|
||||||
# Server
|
# Server
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user