serverless-spy 0.0.36 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.jsii +3 -3
  2. package/cli/cli.ts +145 -75
  3. package/cli/icons/Arch_AWS-Lambda_16.svg +18 -0
  4. package/cli/icons/Arch_Amazon-DynamoDB_16.svg +18 -0
  5. package/cli/icons/Arch_Amazon-EventBridge_16.svg +18 -0
  6. package/cli/icons/Arch_Amazon-Simple-Notification-Service_16.svg +18 -0
  7. package/cli/icons/Arch_Amazon-Simple-Queue-Service_16.svg +18 -0
  8. package/cli/icons/Arch_Amazon-Simple-Storage-Service_16.svg +18 -0
  9. package/cli/index.html +84 -25
  10. package/cli/node_modules/commander/LICENSE +22 -0
  11. package/cli/node_modules/commander/Readme.md +1114 -0
  12. package/cli/node_modules/commander/esm.mjs +16 -0
  13. package/cli/node_modules/commander/index.js +27 -0
  14. package/cli/node_modules/commander/lib/argument.js +147 -0
  15. package/cli/node_modules/commander/lib/command.js +2161 -0
  16. package/cli/node_modules/commander/lib/error.js +45 -0
  17. package/cli/node_modules/commander/lib/help.js +406 -0
  18. package/cli/node_modules/commander/lib/option.js +324 -0
  19. package/cli/node_modules/commander/lib/suggestSimilar.js +100 -0
  20. package/cli/node_modules/commander/package-support.json +16 -0
  21. package/cli/node_modules/commander/package.json +80 -0
  22. package/cli/node_modules/commander/typings/index.d.ts +879 -0
  23. package/cli/package.json +23 -0
  24. package/cli/sampleData.ts +518 -0
  25. package/cli/style.css +66 -42
  26. package/cli/webServerlessSpy.ts +461 -0
  27. package/common/SpyEventSender.ts +291 -0
  28. package/common/getWebSocketUrl.ts +21 -4
  29. package/common/spyEvents/EventBridgeBaseSpyEvent.ts +13 -0
  30. package/common/spyEvents/EventBridgeRuleSpyEvent.ts +2 -7
  31. package/common/spyEvents/EventBridgeSpyEvent.ts +2 -7
  32. package/common/spyEvents/FunctionBaseSpyEvent.ts +7 -0
  33. package/common/spyEvents/FunctionConsole.ts +5 -0
  34. package/common/spyEvents/FunctionConsoleSpyEvent.ts +5 -8
  35. package/common/spyEvents/FunctionResponseSpyEvent.ts +2 -5
  36. package/common/spyEvents/SnsSpyEventBase.ts +11 -0
  37. package/common/spyEvents/SnsSubscriptionSpyEvent.ts +3 -9
  38. package/common/spyEvents/SnsTopicSpyEvent.ts +3 -9
  39. package/dist/releasetag.txt +1 -1
  40. package/extension/interceptor.ts +91 -14
  41. package/functions/sendMessage.ts +4 -2
  42. package/lib/cli/cli.js +124 -65
  43. package/lib/cli/cli.mjs +125 -66
  44. package/lib/cli/sampleData.d.ts +892 -0
  45. package/lib/cli/sampleData.js +481 -0
  46. package/lib/cli/sampleData.mjs +478 -0
  47. package/lib/cli/webServerlessSpy.js +5516 -0
  48. package/lib/cli/webServerlessSpy.js.map +7 -0
  49. package/lib/common/SpyEventSender.d.ts +17 -0
  50. package/lib/common/SpyEventSender.js +227 -0
  51. package/lib/common/SpyEventSender.mjs +223 -0
  52. package/lib/common/getWebSocketUrl.d.ts +1 -1
  53. package/lib/common/getWebSocketUrl.js +19 -7
  54. package/lib/common/getWebSocketUrl.mjs +17 -5
  55. package/lib/common/spyEvents/EventBridgeBaseSpyEvent.d.ts +9 -0
  56. package/lib/common/spyEvents/EventBridgeBaseSpyEvent.js +3 -0
  57. package/lib/common/spyEvents/EventBridgeBaseSpyEvent.mjs +2 -0
  58. package/lib/common/spyEvents/EventBridgeRuleSpyEvent.d.ts +2 -7
  59. package/lib/common/spyEvents/EventBridgeRuleSpyEvent.js +1 -1
  60. package/lib/common/spyEvents/EventBridgeRuleSpyEvent.mjs +1 -1
  61. package/lib/common/spyEvents/EventBridgeSpyEvent.d.ts +2 -7
  62. package/lib/common/spyEvents/EventBridgeSpyEvent.js +1 -1
  63. package/lib/common/spyEvents/EventBridgeSpyEvent.mjs +1 -1
  64. package/lib/common/spyEvents/FunctionBaseSpyEvent.d.ts +6 -0
  65. package/lib/common/spyEvents/FunctionBaseSpyEvent.js +3 -0
  66. package/lib/common/spyEvents/FunctionBaseSpyEvent.mjs +2 -0
  67. package/lib/common/spyEvents/FunctionConsole.d.ts +5 -0
  68. package/lib/common/spyEvents/FunctionConsole.js +3 -0
  69. package/lib/common/spyEvents/FunctionConsole.mjs +2 -0
  70. package/lib/common/spyEvents/FunctionConsoleSpyEvent.d.ts +4 -8
  71. package/lib/common/spyEvents/FunctionConsoleSpyEvent.js +1 -1
  72. package/lib/common/spyEvents/FunctionConsoleSpyEvent.mjs +1 -1
  73. package/lib/common/spyEvents/FunctionResponseSpyEvent.d.ts +2 -5
  74. package/lib/common/spyEvents/FunctionResponseSpyEvent.js +1 -1
  75. package/lib/common/spyEvents/FunctionResponseSpyEvent.mjs +1 -1
  76. package/lib/common/spyEvents/SnsSpyEventBase.d.ts +10 -0
  77. package/lib/common/spyEvents/SnsSpyEventBase.js +3 -0
  78. package/lib/common/spyEvents/SnsSpyEventBase.mjs +2 -0
  79. package/lib/common/spyEvents/SnsSubscriptionSpyEvent.d.ts +2 -9
  80. package/lib/common/spyEvents/SnsSubscriptionSpyEvent.js +1 -1
  81. package/lib/common/spyEvents/SnsSubscriptionSpyEvent.mjs +1 -1
  82. package/lib/common/spyEvents/SnsTopicSpyEvent.d.ts +2 -9
  83. package/lib/common/spyEvents/SnsTopicSpyEvent.js +1 -1
  84. package/lib/common/spyEvents/SnsTopicSpyEvent.mjs +1 -1
  85. package/lib/extension/dist/layer/nodejs/node_modules/interceptor.js +251 -181
  86. package/lib/extension/dist/layer/nodejs/node_modules/interceptor.js.map +3 -3
  87. package/lib/listener/SpyHandlers.ts.d.ts +30 -2
  88. package/lib/listener/SpyHandlers.ts.js +1 -1
  89. package/lib/listener/SpyHandlers.ts.mjs +1 -1
  90. package/lib/listener/WsListener.js +11 -11
  91. package/lib/listener/WsListener.mjs +12 -12
  92. package/lib/src/ServerlessSpy.js +2 -3
  93. package/lib/src/ServerlessSpy.mjs +1 -2
  94. package/listener/SpyHandlers.ts.ts +70 -9
  95. package/listener/WsListener.ts +21 -18
  96. package/package.json +5 -3
  97. package/cli/serverlessSpy.js +0 -73
  98. package/cli/ws.ts +0 -79
  99. package/common/publishSpyEvent.ts +0 -269
  100. package/lib/cli/ws.d.ts +0 -1
  101. package/lib/cli/ws.js +0 -68
  102. package/lib/cli/ws.mjs +0 -66
  103. package/lib/common/publishSpyEvent.d.ts +0 -4
  104. package/lib/common/publishSpyEvent.js +0 -211
  105. package/lib/common/publishSpyEvent.mjs +0 -205
@@ -1,6 +1,7 @@
1
1
  import { DynamoDBSpyEvent } from '../common/spyEvents/DynamoDBSpyEvent';
2
2
  import { EventBridgeRuleSpyEvent } from '../common/spyEvents/EventBridgeRuleSpyEvent';
3
3
  import { EventBridgeSpyEvent } from '../common/spyEvents/EventBridgeSpyEvent';
4
+ import { FunctionConsole } from '../common/spyEvents/FunctionConsole';
4
5
  import { FunctionConsoleSpyEvent } from '../common/spyEvents/FunctionConsoleSpyEvent';
5
6
  import { FunctionContext } from '../common/spyEvents/FunctionContext';
6
7
  import { FunctionRequestSpyEvent } from '../common/spyEvents/FunctionRequestSpyEvent';
@@ -112,13 +113,26 @@ export interface FunctionBaseSpyHandler<TData = any>
112
113
  FunctionRequestSpyEvent<TData>,
113
114
  FunctionRequestSpyHandler<TData>
114
115
  > {
115
- // follwedByConsoleLog: () => Promise<
116
- // FunctionBaseSpyHandler<TData> &
117
- // JestExpectWithSpyMethods<
118
- // FunctionRequestSpyEvent<TData>,
119
- // FunctionRequestSpyHandler<TData>
120
- // >
121
- // >;
116
+ followedByConsole: (
117
+ // This implementation confuses TypeScript which does not accurately display the type.
118
+ param?: {
119
+ condition?: (event: {
120
+ spyEventType: 'FunctionConsole';
121
+ request: TData;
122
+ context: FunctionContext;
123
+ console: FunctionConsole;
124
+ }) => boolean;
125
+ timoutMs?: number;
126
+ }
127
+ ) => Promise<
128
+ FunctionBaseSpyHandler<TData> &
129
+ FunctionConsoleSpyHandler<TData> &
130
+ JestExpectWithSpyMethods<
131
+ FunctionConsoleSpyEvent<TData>,
132
+ FunctionRequestSpyHandler<TData>
133
+ >
134
+ >;
135
+
122
136
  followedByResponse: <TDataResponse = any>(
123
137
  // This implementation confuses TypeScript which does not accurately display the type.
124
138
  // Leave it for reference!
@@ -137,7 +151,7 @@ export interface FunctionBaseSpyHandler<TData = any>
137
151
  timoutMs?: number;
138
152
  }
139
153
  ) => Promise<
140
- FunctionResponseSpyHandler &
154
+ FunctionResponseSpyHandler<TData> &
141
155
  JestExpectWithSpyMethods<
142
156
  FunctionRequestSpyEvent<TData>,
143
157
  FunctionRequestSpyHandler<TData>
@@ -151,8 +165,55 @@ export interface FunctionRequestSpyHandler<TData = any>
151
165
  }
152
166
 
153
167
  export interface FunctionConsoleSpyHandler<TData = any>
154
- extends FunctionBaseSpyHandler<TData> {
168
+ extends JestExpectWithSpyMethods<
169
+ FunctionConsoleSpyEvent<TData>,
170
+ FunctionConsoleSpyHandler<TData>
171
+ > {
155
172
  getData: () => PrettifyForDisplay<FunctionConsoleSpyEvent<TData>>;
173
+ followedByConsole: (
174
+ // This implementation confuses TypeScript which does not accurately display the type.
175
+ param?: {
176
+ condition?: (event: {
177
+ spyEventType: 'FunctionConsole';
178
+ request: TData;
179
+ context: FunctionContext;
180
+ console: FunctionConsole;
181
+ }) => boolean;
182
+ timoutMs?: number;
183
+ }
184
+ ) => Promise<
185
+ FunctionBaseSpyHandler<TData> &
186
+ FunctionConsoleSpyHandler<TData> &
187
+ JestExpectWithSpyMethods<
188
+ FunctionConsoleSpyEvent<TData>,
189
+ FunctionRequestSpyHandler<TData>
190
+ >
191
+ >;
192
+
193
+ followedByResponse: <TDataResponse = any>(
194
+ // This implementation confuses TypeScript which does not accurately display the type.
195
+ // Leave it for reference!
196
+ // param: PrettifyForDisplay<
197
+ // WaitForParams<
198
+ // PrettifyForDisplay<FunctionResponseSpyEvent<TData, TDataResponse>>
199
+ // >
200
+ // >
201
+ param?: {
202
+ condition?: (event: {
203
+ spyEventType: 'FunctionResponse';
204
+ request: TData;
205
+ context: FunctionContext;
206
+ response: TDataResponse;
207
+ }) => boolean;
208
+ timoutMs?: number;
209
+ }
210
+ ) => Promise<
211
+ FunctionResponseSpyHandler<TData> &
212
+ JestExpectWithSpyMethods<
213
+ FunctionRequestSpyEvent<TData>,
214
+ FunctionRequestSpyHandler<TData>
215
+ >
216
+ >;
156
217
  }
157
218
 
158
219
  export interface FunctionResponseSpyHandler<TData = any>
@@ -1,5 +1,5 @@
1
1
  import { WebSocket } from 'ws';
2
- import { getWebSocketUrl } from '../common/getWebSocketUrl';
2
+ import { getSignedWebSocketUrl } from '../common/getWebSocketUrl';
3
3
  import { FunctionRequestSpyEvent } from '../common/spyEvents/FunctionRequestSpyEvent';
4
4
  import { SpyEvent } from '../common/spyEvents/SpyEvent';
5
5
  import { SpyMessage } from '../common/spyEvents/SpyMessage';
@@ -25,7 +25,7 @@ export class WsListener<TSpyEvents> {
25
25
  this.connectionOpenResolve = resolve;
26
26
  });
27
27
 
28
- const urlSigned = await getWebSocketUrl(
28
+ const urlSigned = await getSignedWebSocketUrl(
29
29
  params.serverlessSpyWsUrl,
30
30
  params.credentials
31
31
  );
@@ -90,27 +90,30 @@ export class WsListener<TSpyEvents> {
90
90
  (serviceKeyForFunction.endsWith('Request') ||
91
91
  serviceKeyForFunction.endsWith('Console'))
92
92
  ) {
93
- let serviceKeyForFunctionResponse = serviceKeyForFunction;
94
-
95
- if (serviceKeyForFunctionResponse.endsWith('Request')) {
96
- serviceKeyForFunctionResponse =
97
- serviceKeyForFunctionResponse.substring(
98
- 0,
99
- serviceKeyForFunctionResponse.length - 'Request'.length
100
- );
101
- } else if (serviceKeyForFunctionResponse.endsWith('Console')) {
102
- serviceKeyForFunctionResponse =
103
- serviceKeyForFunctionResponse.substring(
104
- 0,
105
- serviceKeyForFunctionResponse.length - 'Console'.length
106
- );
93
+ let serviceKeyForFunctionChain = serviceKeyForFunction;
94
+
95
+ if (serviceKeyForFunctionChain.endsWith('Request')) {
96
+ serviceKeyForFunctionChain = serviceKeyForFunctionChain.substring(
97
+ 0,
98
+ serviceKeyForFunctionChain.length - 'Request'.length
99
+ );
100
+ } else if (serviceKeyForFunctionChain.endsWith('Console')) {
101
+ serviceKeyForFunctionChain = serviceKeyForFunctionChain.substring(
102
+ 0,
103
+ serviceKeyForFunctionChain.length - 'Console'.length
104
+ );
107
105
  }
108
106
 
109
- serviceKeyForFunctionResponse += 'Response';
107
+ spyAndJestMatchers.followedByConsole = (paramsW: WaitForParams) => {
108
+ return this.createWaitForXXXFunc(
109
+ `${serviceKeyForFunctionChain}Console`,
110
+ (message.data as FunctionRequestSpyEvent).context.awsRequestId
111
+ )(paramsW);
112
+ };
110
113
 
111
114
  spyAndJestMatchers.followedByResponse = (paramsW: WaitForParams) => {
112
115
  return this.createWaitForXXXFunc(
113
- serviceKeyForFunctionResponse,
116
+ `${serviceKeyForFunctionChain}Response`,
114
117
  (message.data as FunctionRequestSpyEvent).context.awsRequestId
115
118
  )(paramsW);
116
119
  };
package/package.json CHANGED
@@ -32,7 +32,8 @@
32
32
  "lint": "eslint **/*.ts",
33
33
  "lint-fix": "eslint --fix './**/*.ts'",
34
34
  "pretty": "prettier --write '**/*.ts'",
35
- "bundle-extension": "scripts/run-task bundle-extension"
35
+ "bundle-extension": "scripts/run-task bundle-extension",
36
+ "cli": "scripts/run-task cli"
36
37
  },
37
38
  "author": {
38
39
  "name": "Marko (ServerlessLife.com)",
@@ -78,7 +79,8 @@
78
79
  },
79
80
  "workspaces": [
80
81
  "test",
81
- "test/cdk"
82
+ "test/cdk",
83
+ "cli"
82
84
  ],
83
85
  "peerDependencies": {
84
86
  "aws-cdk-lib": "^2.37.1",
@@ -116,7 +118,7 @@
116
118
  "require": "./lib/index.js"
117
119
  },
118
120
  "license": "Apache-2.0",
119
- "version": "0.0.36",
121
+ "version": "0.0.37",
120
122
  "types": "lib/index.d.ts",
121
123
  "stability": "stable",
122
124
  "jsii": {
@@ -1,73 +0,0 @@
1
- function run() {
2
- const tableBody = document.getElementById("tableBody");
3
- var modal = document.getElementById("myModal");
4
- var span = document.getElementsByClassName("close")[0];
5
- const modalContent = document.getElementById("modalContent");
6
- span.onclick = function () {
7
- modal.style.display = "none";
8
- };
9
- window.onclick = function (event) {
10
- if (event.target == modal) {
11
- modal.style.display = "none";
12
- }
13
- };
14
-
15
- tableBody.addEventListener("click", function (e) {
16
- const dataJson = JSON.stringify(
17
- JSON.parse(e.target.parentElement.children[2].textContent),
18
- null,
19
- 2
20
- );
21
- modalContent.innerText = dataJson;
22
- modal.style.display = "block";
23
- //alert();
24
- });
25
-
26
- const url = "SERVERLESS_SPY_WS_URL";
27
- //"wss://m6g3w6ttdh.execute-api.eu-west-1.amazonaws.com/prod?X-Amz-Security-Token=IQoJb3JpZ2luX2VjELL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCWV1LXdlc3QtMSJGMEQCIDk%2BzQd0RC2DmEgCJEPoRFAkmnhCZl%2BSIlEG4TummRkIAiBIgNG8Yw%2FOAew3XNAE3yrNCK0G0q%2BZI8MwjEa8xoEonirrAQgbEAQaDDA3NjU4NTk2NzA3NSIMxVUxKhwdqGjFOsRtKsgB2eotQMFgl4CrD8uy%2Fe1UE6K4HVc6uWo0gdjgHMPb5C9Mq0wFgHTiDFjFxIFkJpmCiTRXKqNFrx5lpo9C5Ml1bpcv%2BTopT6wvpbFksxChhhnaqUDHiBaGuys8zEy8Bjlu4jF0nKccmUYdieD70%2FXz99OU4wKPxXyWbr0w82y1Xd6ag8g4VNx3EXEdULq7vkM6H2shSAH8EUlZoTD%2BqvIQvyKS8nta3ebhprO%2BsLRQv2YC6k1w46jHuOjtI7hSnmfSKnkUqAxS6g4wgZrFlwY6mQFNSXz7jSF3IRtdcpMEIp%2Bnghf2LhvZKeW6GkZs4DMeuhPjoBehfmXLJIJWgcvLoheQvIt28ddgHU60%2BoRKhZTxQKhpvSOaAbI6M%2B%2FyHvkC2hTR4fEBtESAMnzo8WocfxdaQQ0YVYq2m5NKSXs3QrrsYIIirkFYhB%2BjAwG8bqBAuE9SQNeIthHjT27hB7KUeIIoXtwSVGutv70%3D&X-Amz-Date=20220808T175057Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIARDVHBDXR4QCRLL4O%2F20220808%2Feu-west-1%2Fexecute-api%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=d58ab2b792a69f20f8e3bf9bf4fb19c8ec8f271bd1b594f8cd25c02d4c85521d";
28
- const ws = new WebSocket(url);
29
-
30
- ws.addEventListener("open", function (event) {
31
- console.log("connected " + new Date().toISOString());
32
- });
33
-
34
- ws.addEventListener("message", function ({ data }) {
35
- //debugger;
36
- //console.log(`From server: ${data}`);
37
-
38
- let parsed;
39
- try {
40
- parsed = JSON.parse(data);
41
- } catch (err) {
42
- console.error("Can not parse " + data);
43
- }
44
-
45
- addLog(parsed);
46
-
47
- // if (parsed) {
48
- // console.log(
49
- // `\x1b[47m\x1b[34m${parsed.timestamp} \x1b[31m🍕 ${
50
- // parsed.serviceKey
51
- // }\x1b[0m\x1b[32m\n${JSON.stringify(parsed.data, null, 2)}\x1b[0m`
52
- // );
53
- // }
54
- });
55
- ws.addEventListener("close", function () {
56
- console.log("disconnected " + new Date().toISOString());
57
- });
58
-
59
- function addLog(data) {
60
- const newRow = document.createElement("tr");
61
- newRow.innerHTML = `
62
- <td>${new Date(data.timestamp).toLocaleTimeString()}</td>
63
- <td>${data.serviceKey}</td>
64
- <td>${JSON.stringify(data.data)}</td>
65
- `;
66
- tableBody.append(newRow);
67
- }
68
- }
69
-
70
- document.addEventListener("DOMContentLoaded", function () {
71
- console.log("RUN");
72
- run();
73
- });
package/cli/ws.ts DELETED
@@ -1,79 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as readline from 'readline';
3
- import WebSocket from 'ws';
4
- import { getWebSocketUrl } from '../common/getWebSocketUrl';
5
-
6
- async function run() {
7
- // var credentialsProvider = awsCp.fromTemporaryCredentials();
8
- // const credentials = await credentialsProvider();
9
-
10
- // const client = new STSClient({});
11
- // client.send()
12
-
13
- // const command = new GetSessionTokenCommand({});
14
- // const { Credentials: credentials } = await client.send(command);
15
- // //const credentials = s.Credentials;
16
-
17
- // console.log("credentials", credentials);
18
-
19
- // const url = process.argv[2];
20
- const url = 'wss://preh1xo1xh.execute-api.eu-west-1.amazonaws.com/prod';
21
-
22
- // https://medium.com/@o.bredenberg/iam-sign-your-api-gateway-websocket-connection-request-no-custom-auth-from-your-frontend-65451166757d
23
- const url2 = await getWebSocketUrl(url);
24
-
25
- const ws = new WebSocket(url2);
26
-
27
- const fileDescriptor = fs.openSync('spy_log.json', 'w');
28
- // 📢📁⚡️💾 💽 💾 💿 📀🛢🪣📑🔊
29
-
30
- ws.on('open', () => console.log(`connected ${new Date().toISOString()}`));
31
- ws.on('message', (data: any) => {
32
- // console.log(`From server: ${data}`);
33
-
34
- let parsed;
35
- try {
36
- parsed = JSON.parse(data);
37
- } catch (err) {
38
- console.error(`Can not parse ${data}`);
39
- }
40
-
41
- if (parsed) {
42
- console.log(
43
- `\x1b[47m\x1b[34m${parsed.timestamp} \x1b[31m🍕 ${
44
- parsed.serviceKey
45
- }\x1b[0m\x1b[32m\n${JSON.stringify(parsed.data, null, 2)}\x1b[0m`
46
- );
47
- }
48
-
49
- fs.write(fileDescriptor, data, (error) => {
50
- if (!error) {
51
- // fs.close(fileDescriptor);
52
- } else {
53
- console.error(error);
54
- }
55
- });
56
- fs.write(fileDescriptor, '\n', (error) => {
57
- if (!error) {
58
- // fs.close(fileDescriptor);
59
- } else {
60
- console.error(error);
61
- }
62
- });
63
- });
64
- ws.on('close', () => {
65
- console.log(`disconnected ${new Date().toISOString()}`);
66
- fs.close(fileDescriptor, () => undefined);
67
- process.exit();
68
- });
69
-
70
- readline
71
- .createInterface({
72
- input: process.stdin,
73
- output: process.stdout,
74
- })
75
- .on('line', (data) => {
76
- ws.send(data);
77
- });
78
- }
79
- run().catch(console.error);
@@ -1,269 +0,0 @@
1
- import {
2
- ApiGatewayManagementApi,
3
- PostToConnectionCommand,
4
- } from '@aws-sdk/client-apigatewaymanagementapi';
5
- import {
6
- AttributeValue,
7
- DeleteItemCommand,
8
- DynamoDBClient,
9
- ScanCommand,
10
- } from '@aws-sdk/client-dynamodb';
11
-
12
- import { unmarshall } from '@aws-sdk/util-dynamodb';
13
- import {
14
- DynamoDBStreamEvent,
15
- S3Event,
16
- SNSEvent,
17
- EventBridgeEvent,
18
- SQSEvent,
19
- } from 'aws-lambda';
20
- import { envVariableNames } from '../src/common/envVariableNames';
21
- import { DynamoDBSpyEvent } from './spyEvents/DynamoDBSpyEvent';
22
- import { EventBridgeRuleSpyEvent } from './spyEvents/EventBridgeRuleSpyEvent';
23
- import { EventBridgeSpyEvent } from './spyEvents/EventBridgeSpyEvent';
24
- import { S3SpyEvent } from './spyEvents/S3SpyEvent';
25
- import { SnsSubscriptionSpyEvent } from './spyEvents/SnsSubscriptionSpyEvent';
26
- import { SnsTopicSpyEvent } from './spyEvents/SnsTopicSpyEvent';
27
- import { SpyMessage } from './spyEvents/SpyMessage';
28
- import { SqsSpyEvent } from './spyEvents/SqsSpyEvent';
29
-
30
- const ddb = new DynamoDBClient({
31
- region: process.env.AWS_REGION,
32
- });
33
-
34
- const apigwManagementApi = new ApiGatewayManagementApi({
35
- apiVersion: '2018-11-29',
36
- endpoint: process.env[envVariableNames.SSPY_WS_ENDPOINT]!,
37
- });
38
-
39
- let connections: Record<string, AttributeValue>[] | undefined;
40
-
41
- export async function publishSpyEvent(event: any) {
42
- console.log('EVENT', JSON.stringify(event));
43
-
44
- const mapping = JSON.parse(process.env[envVariableNames.SSPY_INFRA_MAPPING]!);
45
- console.log('mapping', JSON.stringify(mapping));
46
-
47
- let connectionData;
48
-
49
- const scanParams = new ScanCommand({
50
- TableName: process.env[envVariableNames.SSPY_WS_TABLE_NAME] as string,
51
- ProjectionExpression: 'connectionId',
52
- });
53
-
54
- connectionData = await ddb.send(scanParams);
55
-
56
- connections = connectionData.Items;
57
-
58
- console.log(
59
- `connections at ${new Date().toISOString()}`,
60
- JSON.stringify(connections)
61
- );
62
-
63
- const postDataPromises: Promise<any>[] = [];
64
-
65
- if (event?.Records && event.Records[0]?.Sns) {
66
- console.log('*** SNS ***');
67
- const eventSns = event as SNSEvent;
68
- for (const record of eventSns.Records) {
69
- const subscriptionArn = record.EventSubscriptionArn;
70
-
71
- let serviceKey: string;
72
- if (mapping[subscriptionArn]) {
73
- // subscription event that could contain filter based on existing subscription
74
- serviceKey = mapping[subscriptionArn];
75
- } else {
76
- // catch all subscription
77
- const topicArn = record.Sns.TopicArn;
78
- serviceKey = mapping[topicArn];
79
- }
80
-
81
- let message: string;
82
-
83
- try {
84
- message = JSON.parse(record.Sns.Message);
85
- } catch {
86
- message = record.Sns.Message;
87
- }
88
-
89
- const spyEventType = getSpyEventType(serviceKey) as
90
- | 'FunctionSnsTopic'
91
- | 'FunctionSnsSubscription';
92
-
93
- const data: SnsTopicSpyEvent | SnsSubscriptionSpyEvent = {
94
- spyEventType,
95
- message,
96
- subject: record.Sns.Subject,
97
- timestamp: record.Sns.Timestamp,
98
- topicArn: record.Sns.TopicArn,
99
- messageId: record.Sns.MessageId,
100
- messageAttributes: record.Sns.MessageAttributes,
101
- };
102
-
103
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
104
- data,
105
- serviceKey,
106
- };
107
- postDataPromises.push(postData(fluentEvent));
108
- }
109
- } else if (event?.Records && event.Records[0]?.eventSource === 'aws:sqs') {
110
- console.log('*** SQS ***');
111
- const eventSqs = event as SQSEvent;
112
- for (const record of eventSqs.Records) {
113
- const subscriptionArn = record.eventSourceARN;
114
-
115
- const serviceKey = mapping[subscriptionArn];
116
- let body: string;
117
-
118
- try {
119
- body = JSON.parse(record.body);
120
- } catch {
121
- body = record.body;
122
- }
123
-
124
- const data: SqsSpyEvent = {
125
- spyEventType: 'Sqs',
126
- body,
127
- messageAttributes: record.messageAttributes,
128
- };
129
-
130
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
131
- data,
132
- serviceKey,
133
- };
134
- postDataPromises.push(postData(fluentEvent));
135
- }
136
- } else if (event?.Records && event.Records[0]?.s3) {
137
- console.log('*** S3 ***');
138
- const eventS3 = event as S3Event;
139
- for (const record of eventS3.Records) {
140
- const bucketArn = record.s3.bucket.arn;
141
-
142
- const data: S3SpyEvent = {
143
- spyEventType: 'S3',
144
- eventName: record.eventName,
145
- eventTime: record.eventTime,
146
- bucket: record.s3.bucket.name,
147
- key: record.s3.object.key,
148
- };
149
-
150
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
151
- data,
152
- serviceKey: mapping[bucketArn],
153
- };
154
- postDataPromises.push(postData(fluentEvent));
155
- }
156
- } else if (event.Records && event.Records[0]?.dynamodb) {
157
- console.log('*** DYNAMODB ***');
158
- const eventDynamoDB = event as DynamoDBStreamEvent;
159
- for (const record of eventDynamoDB.Records) {
160
- let arn = record.eventSourceARN!;
161
- arn = arn.substring(0, arn.indexOf('/stream/'));
162
-
163
- const data: DynamoDBSpyEvent = {
164
- spyEventType: 'DynamoDB',
165
- eventName: record.eventName,
166
- newImage: unmarshall(record.dynamodb?.NewImage as any),
167
- keys: unmarshall(record.dynamodb?.Keys as any),
168
- oldImage: record.dynamodb?.OldImage
169
- ? unmarshall(record.dynamodb?.OldImage as any)
170
- : undefined,
171
- };
172
-
173
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
174
- data,
175
- serviceKey: mapping[arn],
176
- };
177
- postDataPromises.push(postData(fluentEvent));
178
- }
179
- } else if (
180
- event.detail &&
181
- event['detail-type'] &&
182
- event.version &&
183
- event.source
184
- ) {
185
- console.log('*** EventBridge ***');
186
- const eventEb = event as EventBridgeEvent<any, any>;
187
-
188
- const serviceKey = mapping.eventBridge; // the is new lambda for each subscription
189
-
190
- const spyEventType = getSpyEventType(serviceKey) as
191
- | 'EventBridge'
192
- | 'EventBridgeRule';
193
-
194
- const message = eventEb.detail;
195
-
196
- const data: EventBridgeSpyEvent | EventBridgeRuleSpyEvent = {
197
- spyEventType,
198
- detail: message,
199
- detailType: eventEb['detail-type'],
200
- source: eventEb.source,
201
- time: eventEb.time,
202
- account: eventEb.account,
203
- };
204
-
205
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
206
- data,
207
- serviceKey,
208
- };
209
- postDataPromises.push(postData(fluentEvent));
210
- } else {
211
- console.log('*** OTHER ***');
212
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = event;
213
- postDataPromises.push(postData(fluentEvent));
214
- }
215
-
216
- await Promise.all(postDataPromises);
217
- }
218
-
219
- export async function postData(spyMessage: Omit<SpyMessage, 'timestamp'>) {
220
- // const postData = JSON.parse(event.body!).data;
221
- console.log('postData', JSON.stringify(spyMessage));
222
-
223
- if (!connections) {
224
- return;
225
- }
226
-
227
- const postCalls = connections.map(async ({ connectionId }) => {
228
- console.log(`Sending message to: ${connectionId.S}`);
229
-
230
- try {
231
- const postToConnectionCommand = new PostToConnectionCommand({
232
- ConnectionId: connectionId.S,
233
- Data: JSON.stringify({
234
- timestamp: new Date().toISOString(),
235
- serviceKey: spyMessage.serviceKey,
236
- data: spyMessage.data,
237
- }) as any,
238
- });
239
-
240
- await apigwManagementApi.send(postToConnectionCommand);
241
- } catch (e) {
242
- console.error(`Fails sending message to: ${connectionId.S}`, e);
243
- console.error(`Status code: ${(e as any).statusCode}`);
244
- if ((e as any).$metadata.httpStatusCode === 410) {
245
- console.log(`Found stale connection, deleting ${connectionId}`);
246
-
247
- const deleteParams = new DeleteItemCommand({
248
- TableName: process.env[envVariableNames.SSPY_WS_TABLE_NAME],
249
- Key: { connectionId },
250
- });
251
-
252
- await ddb.send(deleteParams);
253
- } else {
254
- throw e;
255
- }
256
- }
257
- });
258
-
259
- await Promise.all(postCalls);
260
- console.log('Send message finish');
261
- }
262
-
263
- export function getSpyEventType(serviceKey: string) {
264
- if (!serviceKey) {
265
- throw new Error('Missing serviceKey');
266
- }
267
-
268
- return serviceKey.substring(0, serviceKey.indexOf('#'));
269
- }
package/lib/cli/ws.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};