-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bba37ad
Showing
13 changed files
with
4,415 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"plugins": ["transform-async-to-generator"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
npm-debug.log | ||
.DS_Store | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# micro-chain | ||
|
||
Helps to build chains from your [micro](https://github.com/zeit/micro/) services. | ||
|
||
### Example | ||
|
||
**index.js** | ||
```js | ||
const chain = require('micro-chain'); | ||
|
||
const options = { | ||
micro: 'app.js', // or just your micro async function required before | ||
location: /\/some-path$/, // optional. chain will be executed only if request url matched | ||
chain: [{ | ||
target: 'account.api.your-domain.com', | ||
mergeJson: true // response json will be merged with | ||
// request json and will be passed down the chain | ||
}, { | ||
target: 'storage.api.your-domain.com/s3/save', | ||
mergeJson: true, | ||
// will modify request data and return result if declared | ||
// receive Buffer | ||
transformRequestBody: body => { | ||
if (!body || !body.length) return body; | ||
body = JSON.parse(body); | ||
delete body.secret; | ||
return body; | ||
}, | ||
// will modify response body if declared | ||
// receive Buffer | ||
transformReponseBody: body => { | ||
if (!body || !body.length) return body; | ||
body = JSON.parse(body); | ||
body.newProp = true; | ||
delete body.token; | ||
return body; | ||
} | ||
}, { | ||
// if host is not declared will extract host from original request | ||
target: '/notify' | ||
}, { | ||
target: 'some-stuff.api.your-domain.com', | ||
sendOriginalBody: true // modifed body passed down the chain. | ||
// use this option if you want send original data | ||
}] | ||
}; | ||
|
||
module.exports = chain(options); | ||
``` | ||
|
||
Run: | ||
```bash | ||
micro index.js | ||
``` | ||
|
||
### Author | ||
[Dmitry Pavlovsky](http://palosk.in) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
const { json } = require('micro'); | ||
|
||
module.exports = async req => { | ||
const uri = req.url; | ||
switch (uri) { | ||
case '/auth': { | ||
return { id: '123', username: 'paloskin', email: '[email protected]' }; | ||
} | ||
case '/private': { | ||
return { message: 'Nice to see you :)' }; | ||
} | ||
case '/public': { | ||
const body = await json(req); | ||
return Object.assign({}, body, { final: true }); | ||
} | ||
default: | ||
return { message: 'hi' }; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
const chain = require('./../lib'); | ||
|
||
const options = { | ||
micro: './app.js', | ||
location: /\/public$/, | ||
chain: [{ | ||
target: '/private' | ||
}, { | ||
target: '/auth', | ||
allowedStatuses: [200], | ||
mergeJson: true, | ||
transformRequestBody: body => { | ||
body = JSON.parse(body); | ||
delete body.password; | ||
return body; | ||
}, | ||
transformResponseBody: body => { | ||
body = JSON.parse(body); | ||
delete body.email; | ||
return body; | ||
} | ||
}, { | ||
target: 'https://ping.pong.global', | ||
mergeJson: true | ||
}, { | ||
target: 'http://date.jsontest.com', | ||
mergeJson: true | ||
}] | ||
}; | ||
|
||
module.exports = chain(options); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
const http = require('http'); | ||
const https = require('https'); | ||
const url = require('url'); | ||
const querystring = require('querystring'); | ||
const Promise = require('bluebird'); | ||
const typer = require('media-typer'); | ||
const bl = require('bl'); | ||
const fullUrl = require('full-url'); | ||
const ip = require('ip'); | ||
|
||
module.exports = (req, body, { | ||
target, | ||
useSearchQuery = false, | ||
isOriginal = false | ||
} = {}) => { | ||
return new Promise((resolve, reject) => { | ||
const type = req.headers['content-type']; | ||
const encoding = (type) ? typer.parse(type).parameters.charset : undefined; | ||
|
||
const full = fullUrl(req); | ||
const parsedOriginal = url.parse(full); | ||
|
||
if (!target.startsWith('http:') && !target.startsWith('https:') && !target.startsWith('/')) { | ||
target = `${parsedOriginal.protocol}//${target}`; | ||
} | ||
|
||
const parsedTarget = url.parse(target); | ||
if (!parsedTarget.protocol) { | ||
parsedTarget.protocol = parsedOriginal.protocol; | ||
} | ||
|
||
// Don't DOS Yourself! | ||
if (!parsedTarget.hostname) { | ||
const p = url.parse(full); | ||
parsedTarget.hostname = p.hostname; | ||
parsedTarget.port = p.port; | ||
} | ||
|
||
const headers = Object.assign({}, req.headers, { | ||
host: parsedTarget.host || parsedOriginal.host | ||
}); | ||
|
||
if (body && body.length !== 0) { | ||
headers['content-length'] = body.length; | ||
} else if (headers['content-length']) { | ||
delete headers['content-length']; | ||
} | ||
|
||
let resolvedUrl = parsedTarget.path; | ||
if (useSearchQuery) { | ||
const queryObj = Object.assign({}, querystring.parse(parsedTarget.query), querystring.parse(parsedOriginal.query)); | ||
resolvedUrl = url.format({ | ||
pathname: parsedTarget.pathname, | ||
search: querystring.stringify(queryObj) | ||
}); | ||
} | ||
|
||
const requestOptions = { | ||
protocol: isOriginal ? 'http:' : parsedTarget.protocol, | ||
hostname: isOriginal ? ip.address() : parsedTarget.hostname, | ||
port: parsedTarget.port || ((parsedTarget.protocol === 'http:') ? 80 : 443), | ||
path: resolvedUrl, | ||
method: req.method, | ||
headers | ||
}; | ||
|
||
const request = (requestOptions.protocol === 'https:' ? https : http).request(requestOptions, res => { | ||
res.setEncoding(encoding); | ||
res.pipe(bl((err, body) => { | ||
if (err) { | ||
return reject(err); | ||
} | ||
|
||
resolve({ | ||
body, | ||
statusCode: res.statusCode, | ||
headers: res.headers | ||
}); | ||
})); | ||
}); | ||
|
||
request.on('error', err => { | ||
reject(err); | ||
}); | ||
|
||
request.on('abort', request.abort); | ||
|
||
if (body) { | ||
request.write(body); | ||
} | ||
|
||
request.end(); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
const { URL } = require('url'); | ||
const { resolve } = require('path'); | ||
const micro = require('micro'); | ||
const getRawBody = require('raw-body'); | ||
const getPort = require('get-port'); | ||
const fullUrl = require('full-url'); | ||
const doRequest = require('./chain-request'); | ||
|
||
module.exports = opts => { | ||
const mod = (typeof opts.micro === 'function') ? opts.micro : require(resolve(opts.micro)); | ||
let isReady = false; | ||
let port; | ||
|
||
getPort() | ||
.then(p => { | ||
port = p; | ||
micro(mod).listen(port, err => { | ||
if (err) { | ||
console.error(err); | ||
process.exit(1); | ||
} | ||
|
||
isReady = true; | ||
}); | ||
}).catch(err => { | ||
console.error(err); | ||
process.exit(1); | ||
}); | ||
|
||
return async (req, res) => { | ||
const originalBody = await getRawBody(req); | ||
let body = originalBody; | ||
|
||
if (!isReady) { | ||
return micro.send(res, 503, 'Service Unavailable'); | ||
} | ||
|
||
if (opts.chain && | ||
(!opts.location || (opts.location.test(req.url))) | ||
) { | ||
try { | ||
for (let link of opts.chain) { | ||
let bodyToSend = body; | ||
if (link.transformRequestBody) { | ||
bodyToSend = link.transformRequestBody(bodyToSend); | ||
if (typeof bodyToSend === 'object' && !(bodyToSend instanceof Buffer)) { | ||
bodyToSend = JSON.stringify(bodyToSend); | ||
} | ||
} | ||
|
||
const result = await doRequest(req, (link.sendOriginalBody ? originalBody : bodyToSend), link); | ||
const allowedStatuses = link.allowedStatuses || [200]; | ||
|
||
if (!allowedStatuses.includes(result.statusCode)) { | ||
return micro.send(res, result.statusCode, result.body); | ||
} | ||
|
||
if (typeof link.transformResponseBody === 'function') { | ||
result.body = link.transformResponseBody(result.body); | ||
if (typeof result.body === 'object') { | ||
result.body = JSON.stringify(result.body); | ||
} | ||
} | ||
|
||
if (link.mergeJson) { | ||
try { | ||
body = Object.assign({}, JSON.parse(body), JSON.parse(result.body)); | ||
body = JSON.stringify(body); | ||
} catch (err) {} | ||
} | ||
} | ||
} catch (err) { | ||
console.error(err); | ||
throw micro.createError(500, 'Internal Server Error'); | ||
} | ||
} | ||
|
||
const full = new URL(fullUrl(req)); | ||
full.port = port; | ||
|
||
const originalRequest = await doRequest(req, body, { target: full.toString(), isOriginal: true }); | ||
|
||
for (let key in originalRequest.headers) { | ||
if ({}.hasOwnProperty.call(originalRequest.headers, key)) { | ||
res.setHeader(key, originalRequest.headers[key]); | ||
} | ||
} | ||
|
||
micro.send(res, originalRequest.statusCode, originalRequest.body); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
{ | ||
"name": "micro-chain", | ||
"version": "0.0.1", | ||
"description": "", | ||
"main": "dist/index.js", | ||
"scripts": { | ||
"start": "cd example && micro index.js", | ||
"build": "babel lib -d dist", | ||
"test": "xo && ava -v test/*.test.js" | ||
}, | ||
"files": [ | ||
"dist", | ||
"README.md" | ||
], | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"bl": "^1.2.0", | ||
"bluebird": "^3.5.0", | ||
"full-url": "^1.0.0", | ||
"get-port": "^2.1.0", | ||
"media-typer": "^0.3.0", | ||
"micro": "latest", | ||
"raw-body": "^2.2.0" | ||
}, | ||
"devDependencies": { | ||
"async-sleep": "^0.1.0", | ||
"ava": "^0.18.2", | ||
"babel-cli": "^6.23.0", | ||
"babel-plugin-transform-async-to-generator": "^6.22.0", | ||
"ip": "^1.1.5", | ||
"request-promise": "^4.1.1", | ||
"xo": "^0.17.1" | ||
}, | ||
"xo": { | ||
"space": true, | ||
"rules": { | ||
"object-curly-spacing": [ | ||
0 | ||
], | ||
"import/no-dynamic-require": [ | ||
0 | ||
], | ||
"unicorn/no-process-exit": [ | ||
0 | ||
], | ||
"max-depth": [ | ||
0 | ||
], | ||
"no-use-extend-native/no-use-extend-native": [ | ||
0 | ||
] | ||
} | ||
} | ||
} |
Oops, something went wrong.