1 | import { jsonSafeParse } from '@middy/util';
|
2 | const defaults = {
|
3 | wrapNumbers: undefined
|
4 | };
|
5 | let _wrapNumbers;
|
6 | const eventNormalizerMiddleware = (opts = {})=>{
|
7 | const { wrapNumbers } = {
|
8 | ...defaults,
|
9 | ...opts
|
10 | };
|
11 | _wrapNumbers = wrapNumbers;
|
12 | const eventNormalizerMiddlewareBefore = async (request)=>{
|
13 | parseEvent(request.event);
|
14 | };
|
15 | return {
|
16 | before: eventNormalizerMiddlewareBefore
|
17 | };
|
18 | };
|
19 | const parseEvent = (event)=>{
|
20 |
|
21 |
|
22 | let eventSource = event.eventSource ?? event.deliveryStreamArn;
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | const records = event.Records ?? event.records ?? event.messages ?? event.tasks ?? event.events;
|
29 | if (!Array.isArray(records)) {
|
30 |
|
31 |
|
32 |
|
33 | eventSource ??= (event.configRuleId && 'aws:config') ?? (event.awslogs && 'aws:cloudwatch') ?? (event['CodePipeline.job'] && 'aws:codepipeline');
|
34 | if (eventSource) {
|
35 | events[eventSource]?.(event);
|
36 | }
|
37 | return;
|
38 | }
|
39 |
|
40 |
|
41 |
|
42 | eventSource ??= records[0].eventSource ?? records[0].EventSource ?? (records[0].s3Key && 'aws:s3:batch');
|
43 | for (const record of records){
|
44 | events[eventSource]?.(record);
|
45 | }
|
46 | };
|
47 | const normalizeS3KeyReplacePlus = /\+/g;
|
48 | const events = {
|
49 | 'aws:amq': (message)=>{
|
50 | message.data = base64Parse(message.data);
|
51 | },
|
52 | 'aws:cloudwatch': (event)=>{
|
53 | event.awslogs.data = base64Parse(event.awslogs.data);
|
54 | },
|
55 | 'aws:codepipeline': (event)=>{
|
56 | event['CodePipeline.job'].data.actionConfiguration.configuration.UserParameters = jsonSafeParse(event['CodePipeline.job'].data.actionConfiguration.configuration.UserParameters);
|
57 | },
|
58 | 'aws:config': (event)=>{
|
59 | event.invokingEvent = jsonSafeParse(event.invokingEvent);
|
60 | event.ruleParameters = jsonSafeParse(event.ruleParameters);
|
61 | },
|
62 |
|
63 | 'aws:dynamodb': (record)=>{
|
64 | record.dynamodb.Keys = unmarshall(record.dynamodb.Keys);
|
65 | record.dynamodb.NewImage = unmarshall(record.dynamodb.NewImage);
|
66 | record.dynamodb.OldImage = unmarshall(record.dynamodb.OldImage);
|
67 | },
|
68 | 'aws:kafka': (event)=>{
|
69 | for(const record in event.records){
|
70 | for (const topic of event.records[record]){
|
71 | topic.value &&= base64Parse(topic.value);
|
72 | }
|
73 | }
|
74 | },
|
75 |
|
76 | 'aws:kinesis': (record)=>{
|
77 | record.kinesis.data = base64Parse(record.kinesis.data);
|
78 | },
|
79 |
|
80 | 'aws:lambda:events': (record)=>{
|
81 | record.data = base64Parse(record.data);
|
82 | },
|
83 | 'aws:s3': (record)=>{
|
84 | record.s3.object.key = normalizeS3Key(record.s3.object.key);
|
85 | },
|
86 | 'aws:s3:batch': (task)=>{
|
87 | task.s3Key = normalizeS3Key(task.s3Key);
|
88 | },
|
89 | SelfManagedKafka: (event)=>{
|
90 | events['aws:kafka'](event);
|
91 | },
|
92 | 'aws:sns': (record)=>{
|
93 | record.Sns.Message = jsonSafeParse(record.Sns.Message);
|
94 | parseEvent(record.Sns.Message);
|
95 | },
|
96 | 'aws:sns:sqs': (record)=>{
|
97 | record.Message = jsonSafeParse(record.Message);
|
98 | parseEvent(record.Message);
|
99 | },
|
100 | 'aws:sqs': (record)=>{
|
101 | record.body = jsonSafeParse(record.body);
|
102 |
|
103 | if (record.body.Type === 'Notification') {
|
104 | events['aws:sns:sqs'](record.body);
|
105 | } else {
|
106 | parseEvent(record.body);
|
107 | }
|
108 | }
|
109 | };
|
110 | const base64Parse = (data)=>jsonSafeParse(Buffer.from(data, 'base64').toString('utf-8'));
|
111 | const normalizeS3Key = (key)=>decodeURIComponent(key.replace(normalizeS3KeyReplacePlus, ' '))
|
112 | ;
|
113 |
|
114 |
|
115 | const unmarshall = (data)=>convertValue.M(data ?? {});
|
116 | const convertValue = {
|
117 | NULL: ()=>null,
|
118 | BOOL: Boolean,
|
119 | N: (value)=>{
|
120 | if (_wrapNumbers) {
|
121 | return {
|
122 | value
|
123 | };
|
124 | }
|
125 | const num = Number(value);
|
126 | if ((Number.MAX_SAFE_INTEGER < num || num < Number.MIN_SAFE_INTEGER) && num !== Number.NEGATIVE_INFINITY && num !== Number.POSITIVE_INFINITY) {
|
127 | try {
|
128 | return BigInt(value);
|
129 | } catch (error) {
|
130 | throw new Error(`${value} can't be converted to BigInt. Set options.wrapNumbers to get string value.`);
|
131 | }
|
132 | }
|
133 | return num;
|
134 | },
|
135 | B: (value)=>value,
|
136 | S: (value)=>value,
|
137 | L: (value)=>value.map((item)=>convertToNative(item)),
|
138 | M: (value)=>Object.entries(value).reduce((acc, [key, value])=>{
|
139 | acc[key] = convertToNative(value);
|
140 | return acc;
|
141 | }, {}),
|
142 | NS: (value)=>new Set(value.map(convertValue.N)),
|
143 | BS: (value)=>new Set(value.map(convertValue.B)),
|
144 | SS: (value)=>new Set(value.map(convertValue.S))
|
145 | };
|
146 | const convertToNative = (data)=>{
|
147 | for (const [key, value] of Object.entries(data)){
|
148 | if (!convertValue[key]) throw new Error(`Unsupported type passed: ${key}`);
|
149 | if (typeof value === 'undefined') continue;
|
150 | return convertValue[key](value);
|
151 | }
|
152 | };
|
153 |
|
154 | export default eventNormalizerMiddleware;
|
155 |
|