UNPKG

7.96 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const command_1 = require("@oclif/command");
4const path = require("path");
5const get_port_1 = require("get-port");
6const fs = require("fs");
7const tsNode = require("ts-node");
8const http = require("http");
9const knex = require("knex");
10const jsonc_parser_1 = require("jsonc-parser");
11const base_command_1 = require("../base-command");
12const filename = process.env.BEARER_LOCAL_DATABASE || ':memory:';
13class Invoke extends base_command_1.default {
14 constructor() {
15 super(...arguments);
16 this.ensureJson = (maybeJson) => {
17 try {
18 JSON.parse(maybeJson);
19 }
20 catch (e) {
21 this.error('Invalid JSON provided');
22 }
23 };
24 this.getFileContent = (filePath) => {
25 const location = path.resolve(filePath);
26 if (filePath) {
27 return fs.existsSync(location)
28 ? fs.readFileSync(location, { encoding: 'utf8' })
29 : this.error(`File not found: ${location}`);
30 }
31 return undefined;
32 };
33 this._startServer = async (port) => {
34 return new Promise((resolve, reject) => {
35 const _server = http
36 .createServer((request, response) => {
37 this.debug('Incoming request');
38 let body = '';
39 request.on('data', chunk => {
40 body += chunk;
41 });
42 request.on('end', async () => {
43 request.method;
44 try {
45 this.debug('method: %s body: %s', request.method, body || '{}');
46 response.setHeader('Connection', 'close');
47 switch (request.method) {
48 case 'GET': {
49 const referenceId = request.url.split('/').reverse()[0];
50 const existingData = await this.getData(referenceId);
51 this.debug('lookup id: %s data:%j', referenceId, existingData);
52 const payload = { Item: { referenceId, ReadAllowed: true } };
53 if (existingData) {
54 payload.Item.data = existingData;
55 }
56 this.debug('GET sending %j', payload);
57 response.write(JSON.stringify(payload));
58 break;
59 }
60 case 'POST':
61 case 'PUT': {
62 const { referenceId, data } = JSON.parse(body);
63 await this.putData(referenceId, data);
64 this.debug('persisting data %j', data);
65 const returnedData = { Item: Object.assign({}, data) };
66 response.write(JSON.stringify(returnedData));
67 }
68 }
69 }
70 catch (e) {
71 this.debug('error:', e);
72 }
73 response.end();
74 });
75 })
76 .listen(port, () => {
77 this.debug('started');
78 resolve(_server);
79 });
80 });
81 };
82 this.getData = async (referenceId) => {
83 const db = await this.db();
84 const rows = await db
85 .table('records')
86 .select('data')
87 .where({ referenceId })
88 .limit(1);
89 if (rows[0]) {
90 return JSON.parse(rows[0].data);
91 }
92 return {};
93 };
94 this.putData = async (referenceId, data) => {
95 const db = await this.db();
96 db.table('records')
97 .where({ referenceId })
98 .delete();
99 await db.table('records').insert({ referenceId, data: JSON.stringify(data) });
100 };
101 }
102 async run() {
103 const { args, flags } = this.parse(Invoke);
104 const funcName = args.Function_Name;
105 this.debug('start invoking: %j', funcName);
106 tsNode.register({
107 project: path.join(this.locator.srcFunctionsDir, 'tsconfig.json')
108 });
109 this.debug('ts-node registered');
110 let func;
111 try {
112 func = require(path.join(this.locator.srcFunctionsDir, funcName)).default;
113 }
114 catch (e) {
115 const funcName = args.Function_Name;
116 if (e.code === 'MODULE_NOT_FOUND') {
117 this.error(`"${funcName}" function does not exist. `);
118 }
119 this.error(e.message);
120 }
121 this.debug('required');
122 const body = flags.data || (flags.file && this.getFileContent(flags.file)) || '{}';
123 this.debug('Injected body, %j', body);
124 const context = this.getFunctionContext();
125 this.ensureJson(body);
126 this.debug('calling');
127 const port = await get_port_1.default();
128 const bearerBaseURL = `http://localhost:${port}`;
129 process.env.bearerBaseURL = bearerBaseURL;
130 this.debug('starting');
131 const server = await this._startServer(port);
132 const datum = await func.init()({
133 body,
134 context: Object.assign({}, context, { bearerBaseURL, isBackend: true // TODO: flag option
135 })
136 });
137 server.close();
138 if (this._db) {
139 this._db.destroy();
140 }
141 // print output
142 console.log(JSON.stringify(datum, null, 2));
143 }
144 getFunctionContext() {
145 const localConfig = this.locator.localConfigPath;
146 const context = {};
147 if (fs.existsSync(localConfig)) {
148 const rawConfig = fs.readFileSync(localConfig, { encoding: 'utf8' });
149 const parsed = jsonc_parser_1.parse(rawConfig);
150 const { setup } = parsed || { setup: null };
151 this.debug('local config: %j', parsed);
152 if (setup && setup.auth) {
153 context.authAccess = setup.auth;
154 }
155 }
156 else {
157 this.debug('no local config found');
158 context.authAccess = {};
159 }
160 return context;
161 }
162 async db() {
163 if (!this._db) {
164 this._db = knex({
165 dialect: 'sqlite3',
166 connection: {
167 filename
168 },
169 pool: { min: 0 },
170 useNullAsDefault: true,
171 debug: /bearer:/.test(process.env.DEBUG || '')
172 });
173 await this._db.schema
174 .hasTable('records')
175 .then(async (exists) => {
176 if (!exists) {
177 await this._db.schema.createTable('records', (table) => {
178 table.index(['referenceId'], 'ref_id_idx');
179 table.string('referenceId');
180 table.text('data');
181 });
182 }
183 })
184 .catch(() => this._db.schema.createTable('records', (table) => {
185 table.index(['referenceId'], 'ref_id_idx');
186 table.string('referenceId');
187 table.text('data');
188 }));
189 }
190 return this._db;
191 }
192}
193Invoke.description = 'invoke function locally';
194Invoke.flags = Object.assign({}, base_command_1.default.flags, { data: command_1.flags.string({ char: 'd' }), file: command_1.flags.string({ char: 'f' }) });
195Invoke.args = [{ name: 'Function_Name', required: true }];
196exports.default = Invoke;