Skip to content

Commit 4406787

Browse files
authored
Merge pull request #23 from ServerlessLife/development
feat: Support Python lambdas (local branch)
2 parents 96cbfe2 + fe6b1f7 commit 4406787

File tree

74 files changed

+26785
-12882
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+26785
-12882
lines changed

.github/workflows/build.yml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.gitignore

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projen/tasks.json

Lines changed: 18 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projenrc.js.bak

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ const project = new awscdk.AwsCdkConstructLibrary({
2525
singleQuote: true,
2626
},
2727
},
28+
jestOptions: {
29+
jestConfig: {
30+
moduleNameMapper: {
31+
['^aws-cdk-lib/.warnings.jsii.js$']: '<rootDir>/node_modules/aws-cdk-lib/.warnings.jsii.js',
32+
},
33+
},
34+
},
2835

2936
// deps: [], /* Runtime dependencies of this module. */
3037
// description: undefined, /* The description is just a string that helps people understand the purpose of the package. */

API.md

Lines changed: 0 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CDK-based library for writing elegant, fast-executing integration tests on AWS s
44

55
# How it works
66

7-
**ServerlessSpy CDK construct creates infrastructure to intercept events in Lambda, SNS, SQS, EventBridge, DynamoDB, S3... and sends it to a testing library or your local web console via API Gateway WebSocket. The testing library subscribes to events so tests can be executed fast without checking/retrying if the process has finished. The testing library is integrated with Jest but can also be used with any other testing library. The web console can be used to see and inspect events in real time.**
7+
**ServerlessSpy CDK construct creates infrastructure to intercept events in Lambda, SNS, SQS, EventBridge, DynamoDB, S3... and sends it to a testing library or your local web console via AWS IoT WebSockets. The testing library subscribes to events so tests can be executed fast without checking/retrying if the process has finished. The testing library is integrated with Jest but can also be used with any other testing library. The web console can be used to see and inspect events in real time.**
88

99
[![Concept](./doc/concept.svg)](https://serverlessspy.com/)
1010

@@ -58,9 +58,12 @@ CDK-based library for writing elegant, fast-executing integration tests on AWS s
5858
# What ServerlessSpy is not
5959
- ServerlessSpy can not be used if your infrastructure is not created with CDK.
6060
- The solution is meant only for the development and (automatic) testing environment. You should **EXCLUDE** ServerlessSpy CDK construct in any other environment, especially a production or a high-load environment. ServerlessSpy is not meant for those; it just induces costs and could contribute to hitting AWS quotas (Lambda concurrent executions, ...).
61-
- Only Node.js stack is supported. There are no plans to support Python or any other. Use of TypeScript is deeply encouraged.
61+
- Node.js and Python stacks are supported. There are no plans to support any other but feel free to contribute. Use of TypeScript is deeply encouraged.
6262
- Web console only runs on your local computer. No cloud hosting of any kind (for now).
6363

64+
# Troubleshooting
65+
When first setting up ServerlessSpy for a new account it can take a couple of minutes before AWS has fully activated AWS IoT. You can tell if it's not activated if you cannot connect using the MQTT Test Client in the AWS IoT console.
66+
6467
# Documentation
6568
- [Quick Start](doc/quick_start.md)
6669
- [CDK Construct](doc/CDK_construct.md)
@@ -81,8 +84,11 @@ CDK-based library for writing elegant, fast-executing integration tests on AWS s
8184
- [Contributing Guide](doc/CONTRIBUTING.md)
8285
- [License](./LICENSE.md)
8386

87+
# Co-authors
88+
- [Marko (ServerlessLife)](https://github.com/ServerlessLife)
89+
- [Hugo Lewenhaupt](https://github.com/Lewenhaupt)
90+
8491
# Contributors (alphabetical)
8592
- [Corentin Doue](https://github.com/CorentinDoue)
86-
- [Hugo Lewenhaupt](https://github.com/Lewenhaupt)
8793
- [Ricardo](https://github.com/cino)
8894
- ⭐⭐⭐ place for your name ⭐⭐⭐

cli/cli.ts

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ import * as fs from 'fs';
33
import * as http from 'http';
44
import * as path from 'path';
55
import { promisify } from 'util';
6+
import { device } from 'aws-iot-device-sdk';
67
import * as progam from 'caporal';
78
import * as open from 'open';
8-
import { getSignedWebSocketUrl } from '../common/getWebSocketUrl';
9+
import { WebSocketServer } from 'ws';
10+
// @ts-ignore
11+
import { getConnection } from '../listener/iot-connection';
12+
import { getTopic } from '../listener/topic';
913

1014
const readFileAsync = promisify(fs.readFile);
1115

@@ -26,7 +30,7 @@ async function run() {
2630
.option('--ws <ws>', 'Websocket link')
2731
.option(
2832
'--cdkoutput <cdkoutput>',
29-
'CDK output file that contains Websocket link in a property ServerlessSpyWsUrl'
33+
'CDK output file that contains IoT Endpoint link in a property ServerlessSpyWsUrl'
3034
)
3135
.option(
3236
'--cdkstack <cdkstack>',
@@ -39,6 +43,12 @@ async function run() {
3943
progam.INT,
4044
'3456'
4145
)
46+
.option(
47+
'--wsport <wsp>',
48+
`A port on localhost where ServerlessSpy websocket is accessible.`,
49+
progam.INT,
50+
'3457'
51+
)
4252
.action((_args, opt, _logger) => {
4353
options = opt;
4454
});
@@ -55,6 +65,56 @@ async function run() {
5565
stackList = Object.keys(cdkOutput);
5666
}
5767

68+
const wss = new WebSocketServer({ port: options.wsport });
69+
let connection: device | undefined = undefined;
70+
71+
wss.on('close', async () => {
72+
if (connection) connection.end(true);
73+
});
74+
75+
wss.on('connection', async function connect(ws) {
76+
console.log('Connection');
77+
ws.on('message', function message(data) {
78+
console.log('received: %s', data);
79+
});
80+
81+
let wsUrl: string | undefined;
82+
if (options.ws) {
83+
wsUrl = options.ws;
84+
} else {
85+
if (cdkOutput && cdkOutput[Object.keys(cdkOutput)[0]]) {
86+
wsUrl = cdkOutput[Object.keys(cdkOutput)[0]].ServerlessSpyWsUrl;
87+
}
88+
}
89+
90+
if (!wsUrl) {
91+
throw new Error('Missing IoT endpoint url');
92+
}
93+
94+
const wsUrlWithoutScope = wsUrl.split('/')[0];
95+
96+
connection = await getConnection(true, wsUrlWithoutScope);
97+
98+
const topic = getTopic('#');
99+
console.log(`Subscribing to ${topic}`);
100+
101+
connection.on('connect', () => {
102+
console.log('Connection opened');
103+
if (connection) {
104+
connection.subscribe(topic);
105+
}
106+
});
107+
108+
connection.on('message', (topic: string, data: Buffer) => {
109+
ws.send(
110+
JSON.stringify({
111+
...JSON.parse(JSON.parse(data.toString()).data),
112+
topic,
113+
})
114+
);
115+
});
116+
});
117+
58118
http
59119
.createServer((request, response) => {
60120
void (async () => {
@@ -122,32 +182,8 @@ async function run() {
122182

123183
response.end(JSON.stringify(stackListAvailable), 'utf-8');
124184
} else if (request.url?.match('^/wsUrl')) {
125-
let wsUrl: string | undefined;
126-
if (options.ws) {
127-
wsUrl = options.ws;
128-
} else {
129-
// options.cdkoutput
130-
const urlPaths = request.url.split('/');
131-
let stack = urlPaths[2];
132-
133-
if (stack) {
134-
wsUrl = cdkOutput[stack].ServerlessSpyWsUrl;
135-
} else {
136-
if (cdkOutput && cdkOutput[Object.keys(cdkOutput)[0]]) {
137-
wsUrl =
138-
cdkOutput[Object.keys(cdkOutput)[0]].ServerlessSpyWsUrl;
139-
}
140-
}
141-
}
142-
143-
if (!wsUrl) {
144-
throw new Error('Missing websocket url');
145-
}
146-
147-
//console.log(`WS URL: ${wsUrl}`);
148-
const signedWSUrl = await getSignedWebSocketUrl(wsUrl);
149185
response.writeHead(200, { 'Content-Type': 'text/html' });
150-
response.end(signedWSUrl, 'utf-8');
186+
response.end(`ws:localhost:${options.wsport}`, 'utf-8');
151187
} else {
152188
try {
153189
const content = await readFileAsync(filePath);

0 commit comments

Comments
 (0)