serverless-spy
Version:
CDK-based library for writing elegant integration tests on AWS serverless architecture and an additional web console to monitor events in real time.
1 lines • 12.7 kB
Source Map (JSON)
{"version":3,"file":"cli.mjs","names":["stackList: string[] | undefined","cdkOutput: Record<string, Record<string, string>>","options: any","connection: device | undefined","wsUrl: string | undefined","filePath: string","mappings: Record<string, string>","error: any","err: any"],"sources":["../../cli/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as fs from 'fs';\nimport * as http from 'http';\nimport * as path from 'path';\nimport { promisify } from 'util';\nimport { device } from 'aws-iot-device-sdk';\nimport * as progam from 'caporal';\nimport * as open from 'open';\nimport { WebSocketServer } from 'ws';\n// @ts-ignore\nimport { getConnection } from '../listener/iot-connection';\nimport { getTopic } from '../listener/topic';\n\nconst readFileAsync = promisify(fs.readFile);\n\n//resolve issue with module import\nlet opener = open;\nif ((open as any).default) {\n opener = (open as any).default;\n}\n\nasync function run() {\n let stackList: string[] | undefined;\n let cdkOutput: Record<string, Record<string, string>>;\n\n let options: any;\n\n progam\n .description('ServerlessSpy web console')\n .option('--ws <ws>', 'Websocket link')\n .option(\n '--cdkoutput <cdkoutput>',\n 'CDK output file that contains IoT Endpoint link in a property ServerlessSpyWsUrl'\n )\n .option(\n '--cdkstack <cdkstack>',\n 'CDK stack in cdk output file. If not specified the first one is picked.'\n )\n .option('--open <open>', 'Open browser', progam.BOOL, true)\n .option(\n '--port <p>',\n `A port on localhost where ServerlessSpy web console is accessible.`,\n progam.INT,\n '3456'\n )\n .option(\n '--wsport <wsp>',\n `A port on localhost where ServerlessSpy websocket is accessible.`,\n progam.INT,\n '3457'\n )\n .action((_args, opt, _logger) => {\n options = opt;\n });\n\n progam.parse(process.argv);\n\n if (!options.ws && !options.cdkoutput) {\n throw new Error('--ws or --cdkoutput parameter not specified');\n }\n\n if (options.cdkoutput) {\n const rawdata = fs.readFileSync(options.cdkoutput);\n cdkOutput = JSON.parse(rawdata.toString());\n stackList = Object.keys(cdkOutput);\n }\n\n const wss = new WebSocketServer({ port: options.wsport });\n let connection: device | undefined = undefined;\n\n wss.on('close', async () => {\n if (connection) connection.end(true);\n });\n\n wss.on('connection', async function connect(ws) {\n console.log('Connection');\n ws.on('message', function message(data) {\n console.log('received: %s', data);\n });\n\n let wsUrl: string | undefined;\n if (options.ws) {\n wsUrl = options.ws;\n } else if (cdkOutput) {\n if (cdkOutput[options.cdkstack]) {\n wsUrl = cdkOutput[options.cdkstack].ServerlessSpyWsUrl;\n } else if (cdkOutput[Object.keys(cdkOutput)[0]]) {\n wsUrl = cdkOutput[Object.keys(cdkOutput)[0]].ServerlessSpyWsUrl;\n }\n }\n\n if (!wsUrl) {\n throw new Error('Missing IoT endpoint url');\n }\n\n const wsUrlWithoutScope = wsUrl.split('/')[0];\n\n connection = await getConnection(true, wsUrlWithoutScope);\n\n const topic = getTopic('#');\n console.log(`Subscribing to ${topic}`);\n\n connection.on('connect', () => {\n console.log('Connection opened');\n if (connection) {\n connection.subscribe(topic);\n }\n });\n\n connection.on('message', (topic: string, data: Buffer) => {\n ws.send(\n JSON.stringify({\n ...JSON.parse(JSON.parse(data.toString()).data),\n topic,\n })\n );\n });\n });\n\n http\n .createServer((request, response) => {\n void (async () => {\n try {\n //console.log('request ', request.url);\n let filePath: string = `.${request.url}`;\n //remove query parameters\n filePath = filePath.split('?')[0];\n let rootFolder = __dirname;\n\n if (request.url?.startsWith('/webServerlessSpy.js')) {\n //get transpiled TS to JS files\n rootFolder = getCompiledJsPath();\n } else if (request.url?.startsWith('/bootstrap/')) {\n filePath = filePath.substring('/bootstrap/'.length);\n const bootstrapFolder = await getNpmModuleInstalledPath(\n 'bootstrap'\n );\n\n rootFolder = bootstrapFolder;\n } else if (request.url?.startsWith('/bootstrap-icons/')) {\n filePath = filePath.substring('/bootstrap-icons/'.length);\n const bootstrapFolder = await getNpmModuleInstalledPath(\n 'bootstrap-icons'\n );\n\n rootFolder = bootstrapFolder;\n } else {\n if (filePath === './') {\n filePath = './index.html';\n }\n }\n\n filePath = path.join(rootFolder, filePath);\n //console.log(`${request.url} --> ${filePath}`);\n\n const extname = String(path.extname(filePath)).toLowerCase();\n const mimeTypes: any = {\n '.html': 'text/html',\n '.js': 'text/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.wav': 'audio/wav',\n '.mp4': 'video/mp4',\n '.woff': 'application/font-woff',\n '.ttf': 'application/font-ttf',\n '.eot': 'application/vnd.ms-fontobject',\n '.otf': 'application/font-otf',\n '.wasm': 'application/wasm',\n };\n\n const contentType = mimeTypes[extname] || 'application/octet-stream';\n\n if (request.url === '/stackList') {\n response.writeHead(200, { 'Content-Type': 'application/json' });\n response.end(JSON.stringify(stackList), 'utf-8');\n } else if (request.url === '/stackTopicMappings') {\n response.writeHead(200, { 'Content-Type': 'application/json' });\n const mappings: Record<string, string> = {};\n if (cdkOutput) {\n for (const [stackName, stack] of Object.entries(cdkOutput)) {\n if (stack.ServerlessSpyWsUrl) {\n const [_, scope] = stack.ServerlessSpyWsUrl.split('/');\n if (scope) {\n mappings[stackName] = scope;\n }\n }\n }\n }\n response.end(JSON.stringify(mappings), 'utf-8');\n } else if (request.url?.match('^/wsUrl')) {\n response.writeHead(200, { 'Content-Type': 'text/html' });\n response.end(`ws:localhost:${options.wsport}`, 'utf-8');\n } else {\n try {\n const content = await readFileAsync(filePath);\n\n response.writeHead(200, { 'Content-Type': contentType });\n response.end(content, 'utf-8');\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n response.writeHead(404, { 'Content-Type': 'text/html' });\n response.end(\n `No such file or directory ${request.url}`,\n 'utf-8'\n );\n } else {\n response.writeHead(500);\n response.end(`Error: ${error.code} ..\\n`);\n }\n }\n }\n } catch (err: any) {\n response.writeHead(500, { 'Content-Type': 'text/html' });\n response.end(err.message, 'utf-8');\n }\n })();\n })\n .listen(options.port);\n\n console.log(\n `ServerlessSpy console runing at http://localhost:${options.port}`\n );\n if (options.open) {\n await opener(`http://localhost:${options.port}`);\n }\n}\n\nrun().catch(console.error);\n\nfunction getNpmModuleInstalledPath(npm: string) {\n let folder = path.join(__dirname, '../', 'node_modules', npm);\n if (fs.existsSync(folder)) {\n return folder;\n }\n\n let folderAsPackage = path.join(__dirname, '../../', 'node_modules', npm);\n\n if (fs.existsSync(folderAsPackage)) {\n return folderAsPackage;\n }\n\n // When boostrap ends up in importing projects root node_modules and not in serverless-spys node_modules\n folderAsPackage = path.join(__dirname, '../../../../', 'node_modules', npm);\n\n if (fs.existsSync(folderAsPackage)) {\n return folderAsPackage;\n }\n\n throw new Error(\n `Can not find package in folder ${folder} and ${folderAsPackage}`\n );\n}\n\nfunction getCompiledJsPath() {\n let folder = path.join(__dirname, '../', 'lib/cli');\n if (fs.existsSync(folder)) {\n return folder;\n }\n\n let folderAsPackage = path.join(__dirname, '../../', 'lib/cli');\n\n if (fs.existsSync(folderAsPackage)) {\n return folderAsPackage;\n }\n\n throw new Error(\n `Can not find compiled files in folder ${folder} and ${folderAsPackage}`\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,gBAAgB,UAAU,GAAG,SAAS;AAG5C,IAAI,SAAS;AACb,IAAK,KAAa,QAChB,UAAU,KAAa;AAGzB,eAAe,MAAM;CACnB,IAAIA;CACJ,IAAIC;CAEJ,IAAIC;AAEJ,QACG,YAAY,4BAA4B,CACxC,OAAO,aAAa,iBAAiB,CACrC,OACC,2BACA,mFACD,CACA,OACC,yBACA,0EACD,CACA,OAAO,iBAAiB,gBAAgB,OAAO,MAAM,KAAK,CAC1D,OACC,cACA,sEACA,OAAO,KACP,OACD,CACA,OACC,kBACA,oEACA,OAAO,KACP,OACD,CACA,QAAQ,OAAO,KAAK,YAAY;AAC/B,YAAU;GACV;AAEJ,QAAO,MAAM,QAAQ,KAAK;AAE1B,KAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,UAC1B,OAAM,IAAI,MAAM,8CAA8C;AAGhE,KAAI,QAAQ,WAAW;EACrB,MAAM,UAAU,GAAG,aAAa,QAAQ,UAAU;AAClD,cAAY,KAAK,MAAM,QAAQ,UAAU,CAAC;AAC1C,cAAY,OAAO,KAAK,UAAU;;CAGpC,MAAM,MAAM,IAAI,gBAAgB,EAAE,MAAM,QAAQ,QAAQ,CAAC;CACzD,IAAIC,aAAiC;AAErC,KAAI,GAAG,SAAS,YAAY;AAC1B,MAAI,WAAY,YAAW,IAAI,KAAK;GACpC;AAEF,KAAI,GAAG,cAAc,eAAe,QAAQ,IAAI;AAC9C,UAAQ,IAAI,aAAa;AACzB,KAAG,GAAG,WAAW,SAAS,QAAQ,MAAM;AACtC,WAAQ,IAAI,gBAAgB,KAAK;IACjC;EAEF,IAAIC;AACJ,MAAI,QAAQ,GACV,SAAQ,QAAQ;WACP,WACT;OAAI,UAAU,QAAQ,UACpB,SAAQ,UAAU,QAAQ,UAAU;YAC3B,UAAU,OAAO,KAAK,UAAU,CAAC,IAC1C,SAAQ,UAAU,OAAO,KAAK,UAAU,CAAC,IAAI;;AAIjD,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,2BAA2B;EAG7C,MAAM,oBAAoB,MAAM,MAAM,IAAI,CAAC;AAE3C,eAAa,MAAM,cAAc,MAAM,kBAAkB;EAEzD,MAAM,QAAQ,SAAS,IAAI;AAC3B,UAAQ,IAAI,kBAAkB,QAAQ;AAEtC,aAAW,GAAG,iBAAiB;AAC7B,WAAQ,IAAI,oBAAoB;AAChC,OAAI,WACF,YAAW,UAAU,MAAM;IAE7B;AAEF,aAAW,GAAG,YAAY,SAAe,SAAiB;AACxD,MAAG,KACD,KAAK,UAAU;IACb,GAAG,KAAK,MAAM,KAAK,MAAM,KAAK,UAAU,CAAC,CAAC,KAAK;IAC/C;IACD,CAAC,CACH;IACD;GACF;AAEF,MACG,cAAc,SAAS,aAAa;AACnC,GAAM,YAAY;AAChB,OAAI;IAEF,IAAIC,WAAmB,IAAI,QAAQ;AAEnC,eAAW,SAAS,MAAM,IAAI,CAAC;IAC/B,IAAI,aAAa;AAEjB,QAAI,QAAQ,KAAK,WAAW,uBAAuB,CAEjD,cAAa,mBAAmB;aACvB,QAAQ,KAAK,WAAW,cAAc,EAAE;AACjD,gBAAW,SAAS,UAAU,GAAqB;AAKnD,kBAJwB,MAAM,0BAC5B,YACD;eAGQ,QAAQ,KAAK,WAAW,oBAAoB,EAAE;AACvD,gBAAW,SAAS,UAAU,GAA2B;AAKzD,kBAJwB,MAAM,0BAC5B,kBACD;eAIG,aAAa,KACf,YAAW;AAIf,eAAW,KAAK,KAAK,YAAY,SAAS;IAG1C,MAAM,UAAU,OAAO,KAAK,QAAQ,SAAS,CAAC,CAAC,aAAa;IAmB5D,MAAM,cAlBiB;KACrB,SAAS;KACT,OAAO;KACP,QAAQ;KACR,SAAS;KACT,QAAQ;KACR,QAAQ;KACR,QAAQ;KACR,QAAQ;KACR,QAAQ;KACR,QAAQ;KACR,SAAS;KACT,QAAQ;KACR,QAAQ;KACR,QAAQ;KACR,SAAS;KACV,CAE6B,YAAY;AAE1C,QAAI,QAAQ,QAAQ,cAAc;AAChC,cAAS,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC/D,cAAS,IAAI,KAAK,UAAU,UAAU,EAAE,QAAQ;eACvC,QAAQ,QAAQ,uBAAuB;AAChD,cAAS,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;KAC/D,MAAMC,WAAmC,EAAE;AAC3C,SAAI,WACF;WAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,UAAU,CACxD,KAAI,MAAM,oBAAoB;OAC5B,MAAM,CAAC,GAAG,SAAS,MAAM,mBAAmB,MAAM,IAAI;AACtD,WAAI,MACF,UAAS,aAAa;;;AAK9B,cAAS,IAAI,KAAK,UAAU,SAAS,EAAE,QAAQ;eACtC,QAAQ,KAAK,MAAM,UAAU,EAAE;AACxC,cAAS,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACxD,cAAS,IAAI,gBAAgB,QAAQ,UAAU,QAAQ;UAEvD,KAAI;KACF,MAAM,UAAU,MAAM,cAAc,SAAS;AAE7C,cAAS,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACxD,cAAS,IAAI,SAAS,QAAQ;aACvBC,OAAY;AACnB,SAAI,MAAM,SAAS,UAAU;AAC3B,eAAS,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACxD,eAAS,IACP,6BAA6B,QAAQ,OACrC,QACD;YACI;AACL,eAAS,UAAU,IAAI;AACvB,eAAS,IAAI,UAAU,MAAM,KAAK,OAAO;;;YAIxCC,KAAU;AACjB,aAAS,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACxD,aAAS,IAAI,IAAI,SAAS,QAAQ;;MAElC;GACJ,CACD,OAAO,QAAQ,KAAK;AAEvB,SAAQ,IACN,oDAAoD,QAAQ,OAC7D;AACD,KAAI,QAAQ,KACV,OAAM,OAAO,oBAAoB,QAAQ,OAAO;;AAIpD,KAAK,CAAC,MAAM,QAAQ,MAAM;AAE1B,SAAS,0BAA0B,KAAa;CAC9C,IAAI,SAAS,KAAK,KAAK,WAAW,OAAO,gBAAgB,IAAI;AAC7D,KAAI,GAAG,WAAW,OAAO,CACvB,QAAO;CAGT,IAAI,kBAAkB,KAAK,KAAK,WAAW,UAAU,gBAAgB,IAAI;AAEzE,KAAI,GAAG,WAAW,gBAAgB,CAChC,QAAO;AAIT,mBAAkB,KAAK,KAAK,WAAW,gBAAgB,gBAAgB,IAAI;AAE3E,KAAI,GAAG,WAAW,gBAAgB,CAChC,QAAO;AAGT,OAAM,IAAI,MACR,kCAAkC,OAAO,OAAO,kBACjD;;AAGH,SAAS,oBAAoB;CAC3B,IAAI,SAAS,KAAK,KAAK,WAAW,OAAO,UAAU;AACnD,KAAI,GAAG,WAAW,OAAO,CACvB,QAAO;CAGT,IAAI,kBAAkB,KAAK,KAAK,WAAW,UAAU,UAAU;AAE/D,KAAI,GAAG,WAAW,gBAAgB,CAChC,QAAO;AAGT,OAAM,IAAI,MACR,yCAAyC,OAAO,OAAO,kBACxD"}