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.
181 lines (179 loc) • 6.35 kB
JavaScript
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
const require_src_common_envVariableNames = require('../src/common/envVariableNames.js');
const require_listener_iot_connection = require('../listener/iot-connection.js');
const require_listener_topic = require('../listener/topic.js');
const require_v4 = require('../node_modules/uuid/dist/esm-node/v4.js');
let __aws_sdk_util_dynamodb = require("@aws-sdk/util-dynamodb");
__aws_sdk_util_dynamodb = require_rolldown_runtime.__toESM(__aws_sdk_util_dynamodb);
//#region common/SpyEventSender.ts
var SpyEventSender = class {
constructor(params) {
this.debugMode = process.env[require_src_common_envVariableNames.envVariableNames.SSPY_DEBUG] === "true";
if (params.log) this.log = params.log;
if (params.logError) this.logError = params.logError;
this.scope = params.scope;
this.iotEndpoint = params.iotEndpoint;
}
async close() {
this.connection?.end();
}
async connect() {
this.connection = await require_listener_iot_connection.getConnection(this.debugMode, this.iotEndpoint);
}
async publishSpyEvent(event) {
this.log("Event", JSON.stringify(event));
const mapping = JSON.parse(process.env[require_src_common_envVariableNames.envVariableNames.SSPY_INFRA_MAPPING]);
this.log("ARN to names mapping", JSON.stringify(mapping));
const postDataPromises = [];
if (event?.Records && event.Records[0]?.Sns) {
const eventSns = event;
for (const record of eventSns.Records) {
const subscriptionArn = record.EventSubscriptionArn;
let serviceKey;
if (mapping[subscriptionArn]) serviceKey = mapping[subscriptionArn];
else serviceKey = mapping[record.Sns.TopicArn];
let message;
try {
message = JSON.parse(record.Sns.Message);
} catch {
message = record.Sns.Message;
}
const fluentEvent = {
data: {
spyEventType: this.getSpyEventType(serviceKey),
message,
subject: record.Sns.Subject,
timestamp: record.Sns.Timestamp,
topicArn: record.Sns.TopicArn,
messageId: record.Sns.MessageId,
messageAttributes: record.Sns.MessageAttributes
},
serviceKey
};
postDataPromises.push(this.postData(fluentEvent));
}
} else if (event?.Records && event.Records[0]?.eventSource === "aws:sqs") {
const eventSqs = event;
for (const record of eventSqs.Records) {
const serviceKey = mapping[record.eventSourceARN];
let body;
try {
body = JSON.parse(record.body);
} catch {
body = record.body;
}
const fluentEvent = {
data: {
spyEventType: "Sqs",
body,
messageAttributes: record.messageAttributes
},
serviceKey
};
postDataPromises.push(this.postData(fluentEvent));
}
} else if (event?.Records && event.Records[0]?.s3) {
const eventS3 = event;
for (const record of eventS3.Records) {
const bucketArn = record.s3.bucket.arn;
const fluentEvent = {
data: {
spyEventType: "S3",
eventName: record.eventName,
eventTime: record.eventTime,
bucket: record.s3.bucket.name,
key: record.s3.object.key
},
serviceKey: mapping[bucketArn]
};
postDataPromises.push(this.postData(fluentEvent));
}
} else if (event.Records && event.Records[0]?.dynamodb) {
const eventDynamoDB = event;
for (const record of eventDynamoDB.Records) {
let arn = record.eventSourceARN;
arn = arn.substring(0, arn.indexOf("/stream/"));
const fluentEvent = {
data: {
spyEventType: "DynamoDB",
eventName: record.eventName,
newImage: record.dynamodb?.NewImage ? (0, __aws_sdk_util_dynamodb.unmarshall)(record.dynamodb?.NewImage) : void 0,
keys: (0, __aws_sdk_util_dynamodb.unmarshall)(record.dynamodb?.Keys),
oldImage: record.dynamodb?.OldImage ? (0, __aws_sdk_util_dynamodb.unmarshall)(record.dynamodb?.OldImage) : void 0
},
serviceKey: mapping[arn]
};
postDataPromises.push(this.postData(fluentEvent));
}
} else if (event.detail && event["detail-type"] && event.version && event.source) {
const eventEb = event;
const serviceKey = mapping.eventBridge;
const fluentEvent = {
data: {
spyEventType: this.getSpyEventType(serviceKey),
detail: eventEb.detail,
detailType: eventEb["detail-type"],
eventBridgeId: eventEb["id"],
source: eventEb.source,
time: eventEb.time,
account: eventEb.account
},
serviceKey
};
postDataPromises.push(this.postData(fluentEvent));
} else {
const fluentEvent = event;
postDataPromises.push(this.postData(fluentEvent));
}
await Promise.all(postDataPromises);
}
encode(input) {
const parts = JSON.stringify(input).match(/.{1,50000}/g);
if (!parts) return [];
this.log(`Encoded iot message, ${parts.length}`);
const id = require_v4.default();
return parts.map((part, index) => ({
id,
index,
count: parts.length,
data: part
}));
}
async postData(spyMessage) {
if (this.connection === void 0) throw new Error("No IoT connection created yet, did you forget to call connect()?");
const withTimeStamp = {
...spyMessage,
timestamp: spyMessage.timestamp || (/* @__PURE__ */ new Date()).toISOString()
};
this.log("Post spy message", JSON.stringify(withTimeStamp));
const connection = this.connection;
const topic = require_listener_topic.getTopic(this.scope);
try {
for (const fragment of this.encode(withTimeStamp)) {
await new Promise((resolve) => {
connection.publish(topic, JSON.stringify(fragment), { qos: 1 }, () => {
this.log("Publishing finished");
resolve();
});
});
this.log(`Published fragment ${fragment.index} out of ${fragment.count} to topic ${topic}`);
}
} catch (e) {
this.logError(`Failed to send payload to iot: ${e}`);
}
this.log("Send spy message finish");
}
getSpyEventType(serviceKey) {
if (!serviceKey) throw new Error("Missing serviceKey");
return serviceKey.substring(0, serviceKey.indexOf("#"));
}
log(message, ...optionalParams) {
if (this.debugMode) console.debug("SSPY EXTENSION", message, ...optionalParams);
}
logError(message, ...optionalParams) {
if (this.debugMode) console.error("SSPY EXTENSION", message, ...optionalParams);
}
};
//#endregion
exports.SpyEventSender = SpyEventSender;
//# sourceMappingURL=SpyEventSender.js.map