UNPKG

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.

154 lines (152 loc) 6.53 kB
#!/usr/bin/env node const __dirname = import.meta.dirname; import { getConnection } from "../listener/iot-connection.mjs"; import { getTopic } from "../listener/topic.mjs"; import * as fs from "fs"; import * as path from "path"; import * as http from "http"; import { promisify } from "util"; import * as progam from "caporal"; import * as open from "open"; import { WebSocketServer } from "ws"; //#region cli/cli.ts const readFileAsync = promisify(fs.readFile); let opener = open; if (open.default) opener = open.default; async function run() { let stackList; let cdkOutput; let options; progam.description("ServerlessSpy web console").option("--ws <ws>", "Websocket link").option("--cdkoutput <cdkoutput>", "CDK output file that contains IoT Endpoint link in a property ServerlessSpyWsUrl").option("--cdkstack <cdkstack>", "CDK stack in cdk output file. If not specified the first one is picked.").option("--open <open>", "Open browser", progam.BOOL, true).option("--port <p>", `A port on localhost where ServerlessSpy web console is accessible.`, progam.INT, "3456").option("--wsport <wsp>", `A port on localhost where ServerlessSpy websocket is accessible.`, progam.INT, "3457").action((_args, opt, _logger) => { options = opt; }); progam.parse(process.argv); if (!options.ws && !options.cdkoutput) throw new Error("--ws or --cdkoutput parameter not specified"); if (options.cdkoutput) { const rawdata = fs.readFileSync(options.cdkoutput); cdkOutput = JSON.parse(rawdata.toString()); stackList = Object.keys(cdkOutput); } const wss = new WebSocketServer({ port: options.wsport }); let connection = void 0; wss.on("close", async () => { if (connection) connection.end(true); }); wss.on("connection", async function connect(ws) { console.log("Connection"); ws.on("message", function message(data) { console.log("received: %s", data); }); let wsUrl; if (options.ws) wsUrl = options.ws; else if (cdkOutput) { if (cdkOutput[options.cdkstack]) wsUrl = cdkOutput[options.cdkstack].ServerlessSpyWsUrl; else if (cdkOutput[Object.keys(cdkOutput)[0]]) wsUrl = cdkOutput[Object.keys(cdkOutput)[0]].ServerlessSpyWsUrl; } if (!wsUrl) throw new Error("Missing IoT endpoint url"); const wsUrlWithoutScope = wsUrl.split("/")[0]; connection = await getConnection(true, wsUrlWithoutScope); const topic = getTopic("#"); console.log(`Subscribing to ${topic}`); connection.on("connect", () => { console.log("Connection opened"); if (connection) connection.subscribe(topic); }); connection.on("message", (topic$1, data) => { ws.send(JSON.stringify({ ...JSON.parse(JSON.parse(data.toString()).data), topic: topic$1 })); }); }); http.createServer((request, response) => { (async () => { try { let filePath = `.${request.url}`; filePath = filePath.split("?")[0]; let rootFolder = __dirname; if (request.url?.startsWith("/webServerlessSpy.js")) rootFolder = getCompiledJsPath(); else if (request.url?.startsWith("/bootstrap/")) { filePath = filePath.substring(11); rootFolder = await getNpmModuleInstalledPath("bootstrap"); } else if (request.url?.startsWith("/bootstrap-icons/")) { filePath = filePath.substring(17); rootFolder = await getNpmModuleInstalledPath("bootstrap-icons"); } else if (filePath === "./") filePath = "./index.html"; filePath = path.join(rootFolder, filePath); const extname = String(path.extname(filePath)).toLowerCase(); const contentType = { ".html": "text/html", ".js": "text/javascript", ".css": "text/css", ".json": "application/json", ".png": "image/png", ".jpg": "image/jpg", ".gif": "image/gif", ".svg": "image/svg+xml", ".wav": "audio/wav", ".mp4": "video/mp4", ".woff": "application/font-woff", ".ttf": "application/font-ttf", ".eot": "application/vnd.ms-fontobject", ".otf": "application/font-otf", ".wasm": "application/wasm" }[extname] || "application/octet-stream"; if (request.url === "/stackList") { response.writeHead(200, { "Content-Type": "application/json" }); response.end(JSON.stringify(stackList), "utf-8"); } else if (request.url === "/stackTopicMappings") { response.writeHead(200, { "Content-Type": "application/json" }); const mappings = {}; if (cdkOutput) { for (const [stackName, stack] of Object.entries(cdkOutput)) if (stack.ServerlessSpyWsUrl) { const [_, scope] = stack.ServerlessSpyWsUrl.split("/"); if (scope) mappings[stackName] = scope; } } response.end(JSON.stringify(mappings), "utf-8"); } else if (request.url?.match("^/wsUrl")) { response.writeHead(200, { "Content-Type": "text/html" }); response.end(`ws:localhost:${options.wsport}`, "utf-8"); } else try { const content = await readFileAsync(filePath); response.writeHead(200, { "Content-Type": contentType }); response.end(content, "utf-8"); } catch (error) { if (error.code === "ENOENT") { response.writeHead(404, { "Content-Type": "text/html" }); response.end(`No such file or directory ${request.url}`, "utf-8"); } else { response.writeHead(500); response.end(`Error: ${error.code} ..\n`); } } } catch (err) { response.writeHead(500, { "Content-Type": "text/html" }); response.end(err.message, "utf-8"); } })(); }).listen(options.port); console.log(`ServerlessSpy console runing at http://localhost:${options.port}`); if (options.open) await opener(`http://localhost:${options.port}`); } run().catch(console.error); function getNpmModuleInstalledPath(npm) { let folder = path.join(__dirname, "../", "node_modules", npm); if (fs.existsSync(folder)) return folder; let folderAsPackage = path.join(__dirname, "../../", "node_modules", npm); if (fs.existsSync(folderAsPackage)) return folderAsPackage; folderAsPackage = path.join(__dirname, "../../../../", "node_modules", npm); if (fs.existsSync(folderAsPackage)) return folderAsPackage; throw new Error(`Can not find package in folder ${folder} and ${folderAsPackage}`); } function getCompiledJsPath() { let folder = path.join(__dirname, "../", "lib/cli"); if (fs.existsSync(folder)) return folder; let folderAsPackage = path.join(__dirname, "../../", "lib/cli"); if (fs.existsSync(folderAsPackage)) return folderAsPackage; throw new Error(`Can not find compiled files in folder ${folder} and ${folderAsPackage}`); } //#endregion export { }; //# sourceMappingURL=cli.mjs.map