import http from 'http'; import https, {ServerOptions} from 'https'; import express from 'express'; import bodyParser from 'body-parser'; import {C4CApiRequest, C4CScfContext} from "../index"; import {APIGatewayProxyResult} from "aws-lambda"; interface EnvConfig { appPath: string, functionEnvVariables: Record, context?: Record, port?: number; protocol?: string; } interface ExpressRequest { headers: Record, method: string, httpVersion: string, connection: { remoteAddress: string }, url: string, body: string | Record } interface ExpressResponse { status: (statusCode: number) => ExpressResponse; send: (value: any) => void; set: (headers: Record) => ExpressResponse; end: (value: any) => void; } const parse = (rawBody: string) => { const params = rawBody.split('&'); const body = {} as Record; for (let param of params) { const str = param.split('='); body[str[0]] = str[1]; } return body; }; export class Simulator { private envConfig: EnvConfig; private entrance?: (request: C4CApiRequest, context: C4CScfContext) => Promise; setEnv(env: Record) { for (let key in env) { process.env[key] = env[key]; } } getDecoratedRequest(req: ExpressRequest, rawBody: string | Record) { const getPathAndParameters = (url: string) => { let pathEndIndex = url.indexOf('?'); pathEndIndex = pathEndIndex > -1 ? pathEndIndex : url.length; return { path: url.substring(0, pathEndIndex), parameters: pathEndIndex === url.length ? {} : parse(url.substring(pathEndIndex + 1)) }; }; const request = {} as C4CApiRequest; const queryObject = getPathAndParameters(req.url); request.headers = req.headers; request.path = queryObject.path; request.httpMethod = req.method; request.requestContext = { requestId: 'mock-request-' + new Date().getTime(), // @ts-ignore envId: this.envConfig.context?.envId, appId: this.envConfig.context?.appId, uin: this.envConfig.context?.uin }; request.queryStringParameters = queryObject.parameters; if (typeof rawBody === 'object') { request.body = JSON.stringify(rawBody); } else { request.body = rawBody; } if (req.headers) { req.headers[`x-forwarded-proto`] = req.headers[`x-client-proto`] = this.envConfig.protocol === 'https' ? 'https' : 'http'; req.headers[`x-client-proto-ver`] = `HTTP/${req.httpVersion}`; req.headers[`x-real-ip`] = req.headers[`x-forwarded-for`] = req.connection.remoteAddress; } req.headers[`isBase64Encoded`] = false; return request; } // resolve(req: Request, res: Express.Response, rawBody: string) { // this.entrance(this.getDecoratedRequest(req, rawBody), {}) // .then((response) => { // res.send(response); // }) // .catch((e) => { // console.error(e.stack); // res.status(500).send('Something broke!'); // }); // } constructor(envConfig: EnvConfig) { // TODO deploy different scfs this.envConfig = envConfig; this.setEnv(envConfig.functionEnvVariables); } deploy(entrance: (request: C4CApiRequest, context: C4CScfContext) => Promise, httpsOptions?: ServerOptions) { this.entrance = entrance; const app = express(); app.use(bodyParser.urlencoded({limit: '50mb', extended: true})); app.use(bodyParser.json({limit: '50mb'})); const self = this; // @ts-ignore app.use((req: ExpressRequest, res: ExpressResponse) => { (self .entrance as (request: C4CApiRequest, context: C4CScfContext) => Promise)(this.getDecoratedRequest(req, req.body) as C4CApiRequest, {} as C4CScfContext) .then((response: APIGatewayProxyResult | string | number | Record) => { if (typeof response === 'string' || typeof response === 'number') { res .status(200) .set({ 'Content-Length': Buffer.byteLength(response + ''), 'Content-Type': 'text/plain' }) .end(response); } else if (typeof response === 'object') { if (response.statusCode) { response.headers = Object.assign( response.headers, response.multiValueHeaders || {} ); if (typeof response.body === 'object') { res .status(response.statusCode) .set(response.headers) .send(response.body); } else { let body; if (response.isBase64Encoded) { body = new Buffer(response.body, 'base64'); } else { body = response.body; } res.status(response.statusCode).set(response.headers).end(body); } } else { res .status(200) .set({ 'Content-Length': Buffer.byteLength(JSON.stringify(response)), 'Content-Type': 'application/json' }) .end(JSON.stringify(response)); } } }) .catch((e) => { console.error(e.stack); res.status(500).send(e.message); }); }); let server; if (this.envConfig.protocol === 'https') { server = https.createServer(httpsOptions || {}, app); } else { server = http.createServer(app); } server.listen(this.envConfig.port || 3001); console.log( `the mock service has been setup at ${ this.envConfig.protocol === 'https' ? 'https' : 'http' }://localhost:${this.envConfig.port || 3001}` ); return server; } }