Skip to content

Commit 21b3cce

Browse files
committed
[api] Finish on beta-API
1 parent 9e64688 commit 21b3cce

File tree

9 files changed

+218
-27
lines changed

9 files changed

+218
-27
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55

66
## [Unreleased][Unreleased]
77

8+
Added:
9+
10+
* Expose API
11+
12+
Changed:
13+
14+
* [maintenance] Update all dependencies
15+
816

917
## [0.3.0][0.3.0] - 2016-06-16
1018

docs/api.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# API Documentation
2+
3+
This API is useful for developers who:
4+
5+
* need a central repository of data on the charges of mobile
6+
money transactions, and/or
7+
* do not wish to re-implement cost calculation and their applications
8+
can rely on network connectivity for this functionality
9+
10+
11+
## Introduction:
12+
13+
The **Base URLs** for the API are `http://mmtc.forfuture.co.ke/api`
14+
(Insecure but more permanent)
15+
and `https://mmtcke-forfutureco.rhcloud.com/api`
16+
(Secure but less permanent).
17+
18+
API Characteristics:
19+
20+
* **JSON**: data back-and-forth is formatted in JSON
21+
* **Status codes**: responses are sent back with sensible status codes
22+
* **Unauthenticated**: no authentication token is required, currently
23+
24+
### Errors:
25+
26+
When the response's status code is **not** 2xx, expect the response
27+
in the following structure:
28+
29+
```json
30+
{
31+
"error": {
32+
"message": "error message",
33+
"name": "ErrorName",
34+
"statusCode": 400
35+
}
36+
}
37+
```
38+
39+
40+
### Endpoints:
41+
42+
* [GET /networks](#get-networks)
43+
* [GET /networks/:network](#get-networks-network)
44+
* [POST /cost](#post-cost)
45+
46+
47+
---
48+
<a href="#get-networks" name="get-networks"># <i class="fa fa-file-text"></i></a>
49+
50+
```http
51+
GET /networks
52+
```
53+
54+
Retrieve data for all networks. This is basically an amalgamation of the
55+
[data files][data-files].
56+
57+
Example partial response:
58+
59+
```http
60+
200 OK
61+
```
62+
63+
```json
64+
{
65+
"networks": [ /* CONTENT of each of the data files */ ]
66+
}
67+
```
68+
69+
70+
---
71+
<a href="#get-networks-network" name="get-networks-network"># <i class="fa fa-file-text"></i></a>
72+
73+
```http
74+
GET /networks/:network
75+
```
76+
77+
Retrieve data for `network`. This basically returns the content of
78+
the data file for `network`.
79+
80+
81+
---
82+
<a href="#post-cost" name="post-cost"># <i class="fa fa-file-text"></i></a>
83+
84+
```http
85+
POST /cost
86+
```
87+
88+
Perform a calculation.
89+
90+
**Parameters:**
91+
92+
* **network**: (Required) target network e.g. "mpesa"
93+
* **amount**: (Required) amount in transaction e.g. 2000
94+
* **transactionType**: (Required) type of transaction e.g. "transfer"
95+
* **transactor**: (Required) target transactor e.g. "agent"
96+
97+
Example response:
98+
99+
```http
100+
200 OK
101+
```
102+
103+
```json
104+
{
105+
"cost": 66
106+
}
107+
```
108+
109+
110+
[data-files]:https://github.com/forfuturellc/mmtc-ke/tree/master/data

docs/news.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
<h2>Our API is open for developers <small>2016-09-01</h2>
2+
3+
We have opened up our [API][api] for developers. It is intended to
4+
be used in applications that do **not** wish to re-implement
5+
calculations on money transactions.
6+
It is quite minimal and currently in development. We welcome
7+
contribution to improve it. Head over to our [Github repository][repo].
8+
9+
[api]:/api/
10+
[repo]:https://github.com/forfuturellc/mmtc-ke
11+
12+
113
<h2>Updated Terms and Conditions <small>2016-06-16</small></h2>
214

315
We have updated our Terms and Conditions to reflect our change

engine/math.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function calculate(name, params) {
5252

5353
network = networks.getNetwork(name);
5454
if (!network) {
55-
throw new errors.NetworkNotFound(`network '${name}' not found`);
55+
throw new errors.NetworkNotFoundError(`network '${name}' not found`);
5656
}
5757

5858
transaction = network.transactions.find(function(t) {

routes/api.js

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ const path = require('path');
1212

1313

1414
// npm-installed modules
15+
const _ = require('lodash');
1516
const Debug = require('debug');
1617
const express = require('express');
1718

1819

1920
// own modules
2021
const engine = require('../engine');
22+
const utils = require('./utils');
2123

2224

2325
// module variables
@@ -31,6 +33,13 @@ exports = module.exports = router;
3133
exports.router = router;
3234

3335

36+
// API doc
37+
router.get('/', function(req, res, next) {
38+
const filepath = path.resolve(__dirname, '../docs/api.md');
39+
return utils.renderMarkdownPage(req, res, next, filepath);
40+
});
41+
42+
3443
// serving data for all networks
3544
router.get('/networks', function(req, res, next) {
3645
return res.json({
@@ -51,13 +60,49 @@ router.get('/networks/:network', function(req, res, next) {
5160
});
5261

5362

63+
// calculations
64+
router.post('/cost', function(req, res, next) {
65+
if (!_.isString(req.body.network)
66+
|| !_.isNumber(req.body.amount)
67+
|| !_.isString(req.body.transactionType)
68+
|| !_.isString(req.body.transactor)) {
69+
const error = new Error('missing/invalid parameter');
70+
error.statusCode = 400;
71+
return next(error);
72+
}
73+
74+
let cost;
75+
try {
76+
cost = engine.math.calculate(req.body.network, req.body);
77+
} catch(ex) {
78+
return next(ex);
79+
}
80+
81+
return res.json({
82+
cost,
83+
});
84+
});
85+
86+
87+
// API 404
88+
router.use(function(req, res, next) {
89+
const error = new Error('API Endpoint Not Found');
90+
error.statusCode = 404;
91+
return next(error);
92+
});
93+
94+
5495
// API Error handler
55-
router.use(function(err, req, res, next) {
56-
err.statusCode = err.statusCode || 500;
57-
if (err.statusCode === 500) {
58-
logger.error(err);
96+
router.use(function(error, req, res, next) {
97+
error.statusCode = error.statusCode || 500;
98+
if (error.statusCode >= 500) {
99+
logger.error(error);
59100
}
60-
return res.status(err.statusCode).json({
61-
error: err,
101+
return res.status(error.statusCode).json({
102+
error: {
103+
message: error.message,
104+
name: error.name,
105+
statusCode: error.statusCode,
106+
},
62107
});
63108
});

routes/public.js

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -128,30 +128,14 @@ router
128128

129129

130130
// News page
131-
router.get("/news", function(req, res, next) {
131+
router.get('/news', function(req, res, next) {
132132
const filepath = path.resolve(__dirname, '../docs/news.md');
133-
return renderMarkdownPage(req, res, next, filepath);
133+
return utils.renderMarkdownPage(req, res, next, filepath);
134134
});
135135

136136

137137
// Terms and conditions
138-
router.use('/tcs', function(req, res, next) {
138+
router.get('/tcs', function(req, res, next) {
139139
const filepath = path.resolve(__dirname, '../docs/terms-and-conditions.md');
140-
return renderMarkdownPage(req, res, next, filepath);
140+
return utils.renderMarkdownPage(req, res, next, filepath);
141141
});
142-
143-
144-
/**
145-
* Render a markdown page
146-
*/
147-
function renderMarkdownPage(req, res, next, filepath) {
148-
return engine.pages.getHTML(filepath, function(getErr, html) {
149-
if (getErr) {
150-
return next(getErr);
151-
}
152-
153-
return utils.renderPage(req, res, 'misc/content', {
154-
content: html,
155-
});
156-
});
157-
}

routes/utils.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ exports = module.exports = {
1717
* @param {Object} [ctx]
1818
*/
1919
renderPage,
20+
/**
21+
* Render a markdown page
22+
*
23+
* @param {Express.Request} req
24+
* @param {Express.Response} res
25+
* @param {Function} next
26+
* @param {String} filepath
27+
*/
28+
renderMarkdownPage,
2029
/**
2130
* Web Middleware
2231
*
@@ -37,6 +46,10 @@ const config = require('config');
3746
const express = require('express');
3847

3948

49+
// own modules
50+
const engine = require("../engine");
51+
52+
4053
function webMiddleware(router, config) {
4154
if (!_.isPlainObject(config)) {
4255
throw new Error('expected configuration to be object');
@@ -59,3 +72,16 @@ function renderPage(req, res, path, ctx) {
5972
ctx.__path = req.path;
6073
return res.render(path, ctx);
6174
}
75+
76+
77+
function renderMarkdownPage(req, res, next, filepath) {
78+
return engine.pages.getHTML(filepath, function(getErr, html) {
79+
if (getErr) {
80+
return next(getErr);
81+
}
82+
83+
return renderPage(req, res, 'misc/content', {
84+
content: html,
85+
});
86+
});
87+
}

web/_includes/head.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
<link rel="stylesheet" href="{{ site.baseurl }}/static/bootstrap-themes/superhero.min.css">
1010
<link rel="stylesheet" href="{{ site.baseurl }}/vendor/animate.css/animate.min.css">
11+
<link rel="stylesheet" href="{{ site.baseurl }}/vendor/font-awesome/css/font-awesome.min.css">
1112

1213
{% set defaultCssURL = '/css/main.css' %}
1314
<link rel="stylesheet" href="{{ site.baseurl }}{{ cssURL or defaultCssURL }}">

web/_includes/header.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
News
2323
</a>
2424
</li>
25+
<li>
26+
<a href="{{ site.baseurl }}/api/">
27+
API
28+
</a>
29+
</li>
2530
</ul>
2631
</div><!--navbar-content-->
2732
</div>

0 commit comments

Comments
 (0)