1 | const debug = require('debug')('upward-js:middleware');
|
2 | const jsYaml = require('js-yaml');
|
3 | const UpwardServerError = require('./UpwardServerError');
|
4 | const IOAdapter = require('./IOAdapter');
|
5 | const buildResponse = require('./buildResponse');
|
6 |
|
7 | class UpwardMiddleware {
|
8 | constructor(upwardPath, env, io) {
|
9 | this.env = env;
|
10 | this.upwardPath = upwardPath;
|
11 | debug(`created for path ${upwardPath}`);
|
12 | this.io = io;
|
13 | }
|
14 | async load() {
|
15 | const { upwardPath } = this;
|
16 | try {
|
17 | this.yamlTxt = await this.io.readFile(upwardPath);
|
18 | } catch (e) {
|
19 | throw new UpwardServerError(e, `unable to read file ${upwardPath}`);
|
20 | }
|
21 | debug(`read upward.yml file successfully`);
|
22 | try {
|
23 | this.definition = await jsYaml.safeLoad(this.yamlTxt);
|
24 | } catch (e) {
|
25 | throw new UpwardServerError(
|
26 | e,
|
27 | `error parsing ${upwardPath} contents: \n\n${this.yamlTxt}`
|
28 | );
|
29 | }
|
30 | debug(`parsed upward.yml file successfully: %o`, this.definition);
|
31 | }
|
32 | async getHandler() {
|
33 | return async (req, res, next) => {
|
34 | const errors = [];
|
35 | let response;
|
36 | try {
|
37 | response = await buildResponse(
|
38 | this.io,
|
39 | this.env,
|
40 | this.definition,
|
41 | req,
|
42 | this.upwardPath
|
43 | );
|
44 | if (typeof response === 'function') {
|
45 | debug('buildResponse returned function');
|
46 | response(req, res, next);
|
47 | return;
|
48 | }
|
49 | if (isNaN(response.status)) {
|
50 | errors.push(
|
51 | `Non-numeric status! Status was '${response.status}'`
|
52 | );
|
53 | }
|
54 | if (typeof response.headers !== 'object') {
|
55 | errors.push(
|
56 | `Resolved with a non-compliant headers object! Headers are: ${
|
57 | response.headers
|
58 | }`
|
59 | );
|
60 | }
|
61 | if (
|
62 | typeof response.body !== 'string' &&
|
63 | typeof response.body.toString !== 'function'
|
64 | ) {
|
65 | errors.push(
|
66 | `Resolved with a non-serializable body! Body was '${Object.prototype.toString.call(
|
67 | response.body
|
68 | )}'`
|
69 | );
|
70 | }
|
71 | } catch (e) {
|
72 | errors.push(e.message);
|
73 | }
|
74 | if (errors.length > 0) {
|
75 | if (this.env.NODE_ENV === 'production') {
|
76 | res.status(500);
|
77 | res.format({
|
78 | json() {
|
79 | res.json({
|
80 | errors: [
|
81 | {
|
82 | status: 500,
|
83 | message: 'Server Error'
|
84 | }
|
85 | ]
|
86 | });
|
87 | },
|
88 | html() {
|
89 | res.send('500 Server Error');
|
90 | }
|
91 | });
|
92 | } else {
|
93 | res.format({
|
94 | json() {
|
95 | res.status(500).json({
|
96 | errors: errors.map(message => ({ message }))
|
97 | });
|
98 | },
|
99 | html() {
|
100 | next(
|
101 | new UpwardServerError(
|
102 | `Request did not evaluate to a valid response, because: \n${errors.join(
|
103 | '\n\n'
|
104 | )}`
|
105 | )
|
106 | );
|
107 | }
|
108 | });
|
109 | }
|
110 | } else {
|
111 | debug('status, headers, and body valid. responding');
|
112 | res.status(response.status)
|
113 | .set(response.headers)
|
114 | .send(
|
115 | typeof response.body === 'string' ||
|
116 | Buffer.isBuffer(response.body)
|
117 | ? response.body
|
118 | : response.body.toString()
|
119 | );
|
120 | }
|
121 | };
|
122 | }
|
123 | }
|
124 |
|
125 | async function upwardJSMiddlewareFactory(
|
126 | upwardPath,
|
127 | env,
|
128 | io = IOAdapter.default(upwardPath)
|
129 | ) {
|
130 | const middleware = new UpwardMiddleware(upwardPath, env, io);
|
131 | await middleware.load();
|
132 | return middleware.getHandler();
|
133 | }
|
134 |
|
135 | upwardJSMiddlewareFactory.UpwardMiddleware = UpwardMiddleware;
|
136 |
|
137 | module.exports = upwardJSMiddlewareFactory;
|