UNPKG

@serverless-offline-queue/plugin-sqs

Version:
306 lines (303 loc) 11.1 kB
var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/libs/ServerlessSqsPlugin.ts import { createSqsServer } from "local-aws-sqs"; import { SQSClient, ReceiveMessageCommand, DeleteMessageBatchCommand } from "@aws-sdk/client-sqs"; var ServerlessSqsPlugin = class { constructor(serverless) { this.serverless = serverless; this.pollingIntervals = /* @__PURE__ */ new Map(); this.hooks = { "before:offline:start": () => __async(this, null, function* () { yield this.startSqsServer(); this.setQueueEnvironmentVariables(); yield this.createLambda(); this.startPolling(); }), "before:offline:start:init": () => __async(this, null, function* () { yield this.startSqsServer(); this.setQueueEnvironmentVariables(); }), "offline:start:init": () => __async(this, null, function* () { yield this.createLambda(); }), "offline:start:ready": () => __async(this, null, function* () { this.startPolling(); }), "after:offline:start:end": () => __async(this, null, function* () { this.stopPolling(); this.stopSqsServer(); }) }; } startSqsServer() { return __async(this, null, function* () { var _a, _b, _c; const pluginConfig = this.getPluginConfig(); const port = (_a = pluginConfig.port) != null ? _a : 9324; const queues = (_c = (_b = pluginConfig.queues) == null ? void 0 : _b.map((queueName) => ({ QueueName: queueName }))) != null ? _c : []; this.sqsServer = yield createSqsServer({ port, queues }); this.sqsClient = new SQSClient({ region: "us-east-1", endpoint: `http://localhost:${port}`, credentials: { accessKeyId: "test", secretAccessKey: "test" } }); if (queues.length > 0) { this.log(`SQS server started on port ${port}. Queues: ${queues.map((q) => q.QueueName).join(", ")}`); } }); } createLambda() { return __async(this, null, function* () { try { const { default: Lambda } = yield import("serverless-offline/lambda"); const lambdas = this.getLambdas(); this.lambda = new Lambda(this.serverless, this.getOfflineOptions()); this.lambda.create(lambdas); this.log(`Lambda functions initialized: ${lambdas.map((l) => l.functionKey).join(", ")}`); } catch (error) { this.logError("Failed to create Lambda instance:", error); } }); } getLambdas() { const service = this.serverless.service; const lambdas = []; const functionKeys = service.getAllFunctions(); functionKeys.forEach((functionKey) => { const functionDefinition = service.getFunction(functionKey); lambdas.push({ functionKey, functionDefinition }); }); return lambdas; } getOfflineOptions() { var _a; const { service: { custom = {}, provider } } = this.serverless; const offlineOptions = (_a = custom["serverless-offline"]) != null ? _a : {}; return __spreadValues(__spreadValues(__spreadValues({}, provider), offlineOptions), this.getPluginConfig()); } setQueueEnvironmentVariables() { var _a, _b; const pluginConfig = this.getPluginConfig(); const port = (_a = pluginConfig.port) != null ? _a : 9324; const baseUrl = `http://localhost:${port}`; const accountId = "000000000000"; const queueUrls = /* @__PURE__ */ new Map(); (_b = pluginConfig.queues) == null ? void 0 : _b.forEach((queueName) => { const queueUrl = `${baseUrl}/${accountId}/${queueName}`; queueUrls.set(queueName, queueUrl); }); const functions = this.serverless.service.functions || {}; Object.entries(functions).forEach(([functionName, functionConfig]) => { var _a2; (_a2 = functionConfig.environment) != null ? _a2 : functionConfig.environment = {}; Object.entries(functionConfig.environment).forEach(([envKey, envValue]) => { if (typeof envValue === "object" && (envValue == null ? void 0 : envValue.Ref)) { const refName = envValue.Ref; const matchingQueue = this.findQueueByResourceName(refName, queueUrls); if (matchingQueue) { functionConfig.environment[envKey] = matchingQueue; } } }); }); } findQueueByResourceName(resourceName, queueUrls) { var _a, _b, _c; const resources = (_b = (_a = this.serverless.service.resources) == null ? void 0 : _a.Resources) != null ? _b : {}; const resource = resources[resourceName]; if ((resource == null ? void 0 : resource.Type) === "AWS::SQS::Queue") { const queueName = (_c = resource.Properties) == null ? void 0 : _c.QueueName; if (queueName && queueUrls.has(queueName)) { return queueUrls.get(queueName); } } return null; } startPolling() { var _a; const functions = (_a = this.serverless.service.functions) != null ? _a : {}; Object.entries(functions).forEach(([functionName, functionConfig]) => { var _a2; const events = (_a2 = functionConfig.events) != null ? _a2 : []; events.forEach((event) => { var _a3; if (event.sqs) { const queueArn = event.sqs.arn; const queueUrl = this.resolveQueueUrl(queueArn); const batchSize = (_a3 = event.sqs.batchSize) != null ? _a3 : 10; if (queueUrl) { const queueName = queueUrl.split("/").pop(); this.log(`Starting SQS polling: ${functionName} -> ${queueName}`); const interval = setInterval(() => { this.pollQueue(queueUrl, functionName, batchSize); }, 1e3); this.pollingIntervals.set(`${functionName}-${queueUrl}`, interval); } else { this.logError(`Unable to resolve queue URL for function: ${functionName}`); } } }); }); } resolveQueueUrl(arn) { var _a, _b, _c, _d; let resourceName = null; if (typeof arn === "object") { if (arn["!GetAtt"]) { resourceName = arn["!GetAtt"][0]; } else if (arn["Fn::GetAtt"]) { resourceName = arn["Fn::GetAtt"][0]; } } if (resourceName) { const resources = (_b = (_a = this.serverless.service.resources) == null ? void 0 : _a.Resources) != null ? _b : {}; const resource = resources[resourceName]; if ((resource == null ? void 0 : resource.Type) === "AWS::SQS::Queue") { const queueName = (_c = resource.Properties) == null ? void 0 : _c.QueueName; if (queueName) { const pluginConfig = this.getPluginConfig(); const port = (_d = pluginConfig.port) != null ? _d : 9324; const accountId = "000000000000"; return `http://localhost:${port}/${accountId}/${queueName}`; } } } return null; } pollQueue(queueUrl, functionName, batchSize) { return __async(this, null, function* () { if (!this.sqsClient) { return; } try { const result = yield this.sqsClient.send(new ReceiveMessageCommand({ QueueUrl: queueUrl, MaxNumberOfMessages: batchSize, WaitTimeSeconds: 0, VisibilityTimeout: 30 })); if (result.Messages && result.Messages.length > 0) { const queueName = queueUrl.split("/").pop(); const sqsEvent = { Records: result.Messages.map((message) => ({ messageId: message.MessageId, receiptHandle: message.ReceiptHandle, body: message.Body, attributes: message.Attributes || {}, messageAttributes: message.MessageAttributes || {}, md5OfBody: message.MD5OfBody, eventSource: "aws:sqs", eventSourceARN: `arn:aws:sqs:us-east-1:000000000000:${queueName}`, awsRegion: "us-east-1" })) }; try { yield this.invokeFunction(functionName, sqsEvent); yield this.sqsClient.send(new DeleteMessageBatchCommand({ QueueUrl: queueUrl, Entries: result.Messages.map((message) => ({ Id: message.MessageId, ReceiptHandle: message.ReceiptHandle })) })); } catch (error) { this.logError(`Function execution error ${functionName}:`, error); } } } catch (error) { this.logError(`Queue polling error:`, error); } }); } invokeFunction(functionName, event) { return __async(this, null, function* () { if (!this.lambda) { this.logError(`Lambda instance not available for function: ${functionName}`); return; } try { const handler = this.lambda.get(functionName); if (!handler) { this.logError(`Handler not found for function: ${functionName}`); return; } handler.setEvent(event); const result = yield handler.runHandler(); return result; } catch (error) { this.logError(`Function invocation error ${functionName}:`, error); throw error; } }); } stopPolling() { this.pollingIntervals.forEach((interval) => { clearInterval(interval); }); this.pollingIntervals.clear(); } stopSqsServer() { if (this.sqsServer) { this.sqsServer.close(); this.sqsServer = void 0; } } getPluginConfig() { var _a; return ((_a = this.serverless.service.custom) == null ? void 0 : _a["@serverless-offline-queue/plugin-sqs"]) || {}; } log(message) { console.log(`[@serverless-offline-queue/plugin-sqs] ${message}`); } logError(message, error) { console.error(`[@serverless-offline-queue/plugin-sqs] ${message}`, error != null ? error : ""); } }; // src/index.ts var index_default = ServerlessSqsPlugin; export { index_default as default };