Skip to content
This repository was archived by the owner on Jan 28, 2025. It is now read-only.

Commit 7bf2c67

Browse files
authored
feat(aws-sqs): support sqs tags/naming (#1543)
1 parent e2588b4 commit 7bf2c67

File tree

12 files changed

+535
-17
lines changed

12 files changed

+535
-17
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/// <reference types="next" />
22
/// <reference types="next/types/global" />
3+
/// <reference types="next/image-types/global" />

packages/e2e-tests/next-app/serverless.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
next-app:
22
component: "../../serverless-components/nextjs-component"
33
inputs:
4+
sqs:
5+
tags:
6+
foo: bar
47
build:
58
postBuildCommands: ["node scripts/post-build-test.js"]
69
assetIgnorePatterns:
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
const promisifyMock = (mockFn) => {
2+
const promise = jest.fn();
3+
mockFn.mockImplementation(() => ({
4+
promise
5+
}));
6+
7+
return promise;
8+
};
9+
10+
export const mockGetCallerIdentity = jest.fn();
11+
export const mockGetCallerIdentityPromise = promisifyMock(
12+
mockGetCallerIdentity
13+
);
14+
15+
export const mockGetQueueAttributes = jest.fn();
16+
export const mockGetQueueAttributesPromise = promisifyMock(
17+
mockGetQueueAttributes
18+
);
19+
20+
export const mockCreateQueue = jest.fn();
21+
export const mockCreateQueuePromise = promisifyMock(mockCreateQueue);
22+
23+
export const mockDeleteQueue = jest.fn();
24+
export const mockDeleteQueuePromise = promisifyMock(mockDeleteQueue);
25+
26+
export const mockListQueueTags = jest.fn();
27+
export const mockListQueueTagsPromise = promisifyMock(mockListQueueTags);
28+
29+
export const mockTagQueue = jest.fn();
30+
export const mockTagQueuePromise = promisifyMock(mockTagQueue);
31+
32+
export const mockUntagQueue = jest.fn();
33+
export const mockUntagQueuePromise = promisifyMock(mockUntagQueue);
34+
35+
export const mockListEventSourceMappings = jest.fn();
36+
export const mockListEventSourceMappingsPromise = promisifyMock(
37+
mockListEventSourceMappings
38+
);
39+
40+
export const mockCreateEventSourceMapping = jest.fn();
41+
export const mockCreateEventSourceMappingPromise = promisifyMock(
42+
mockCreateEventSourceMapping
43+
);
44+
45+
module.exports = {
46+
SQS: jest.fn(() => ({
47+
createQueue: mockCreateQueue,
48+
deleteQueue: mockDeleteQueue,
49+
getQueueAttributes: mockGetQueueAttributes,
50+
listQueueTags: mockListQueueTags,
51+
tagQueue: mockTagQueue,
52+
untagQueue: mockUntagQueue
53+
})),
54+
STS: jest.fn(() => ({
55+
getCallerIdentity: mockGetCallerIdentity
56+
})),
57+
SharedIniFileCredentials: jest.fn(),
58+
Lambda: jest.fn(() => ({
59+
listEventSourceMappings: mockListEventSourceMappings,
60+
createEventSourceMapping: mockCreateEventSourceMapping
61+
})),
62+
mockListEventSourceMappingsPromise,
63+
mockCreateEventSourceMappingPromise,
64+
mockGetCallerIdentityPromise,
65+
mockGetQueueAttributesPromise,
66+
mockCreateQueuePromise,
67+
mockDeleteQueuePromise,
68+
mockListQueueTagsPromise,
69+
mockTagQueuePromise,
70+
mockUntagQueuePromise
71+
};

packages/serverless-components/aws-sqs/__tests__/deploy.test.js

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ const {
77
mockGetCallerIdentityPromise,
88
mockGetQueueAttributesPromise,
99
mockCreateQueuePromise,
10-
mockDeleteQueuePromise
10+
mockDeleteQueuePromise,
11+
mockListQueueTagsPromise,
12+
mockTagQueuePromise,
13+
mockUntagQueuePromise
1114
} = require("aws-sdk");
1215

16+
jest.mock("aws-sdk", () => require("../__mocks__/aws-sqs-aws-sdk.mock"));
17+
1318
describe("sqs component", () => {
1419
const tmpStateFolder = (initialState) => {
15-
const dir = fse.mkdtempSync(path.join(os.tmpdir(), "test-"));
20+
const dir = fse.mkdtempSync(path.join(os.tmpdir(), "test-sqs-"));
1621
if (initialState) {
17-
fse.writeJSONSync(path.join(dir, "TestLambda.json"), initialState);
22+
fse.writeJSONSync(path.join(dir, "TestSqs.json"), initialState);
1823
}
1924
return dir;
2025
};
@@ -30,7 +35,7 @@ describe("sqs component", () => {
3035

3136
it("creates a new queue", async () => {
3237
const dir = tmpStateFolder();
33-
const component = new AwsSqsQueue("TestLambda", {
38+
const component = new AwsSqsQueue("TestSqs", {
3439
stateRoot: dir
3540
});
3641
await component.init();
@@ -43,7 +48,7 @@ describe("sqs component", () => {
4348
mockGetQueueAttributesPromise.mockResolvedValueOnce({
4449
Attributes: { not: "empty" }
4550
});
46-
const component = new AwsSqsQueue("TestLambda", {
51+
const component = new AwsSqsQueue("TestSqs", {
4752
stateRoot: tmpStateFolder({
4853
url: "myQueueUrl"
4954
})
@@ -58,7 +63,7 @@ describe("sqs component", () => {
5863
mockGetQueueAttributesPromise.mockResolvedValueOnce({
5964
Attributes: { not: "empty" }
6065
});
61-
const component = new AwsSqsQueue("TestLambda", {
66+
const component = new AwsSqsQueue("TestSqs", {
6267
stateRoot: tmpStateFolder()
6368
});
6469
await component.init();
@@ -71,7 +76,7 @@ describe("sqs component", () => {
7176
mockListEventSourceMappingsPromise.mockResolvedValueOnce({
7277
EventSourceMappings: [1]
7378
});
74-
const component = new AwsSqsQueue("TestLambda", {
79+
const component = new AwsSqsQueue("TestSqs", {
7580
stateRoot: tmpStateFolder()
7681
});
7782
await component.init();
@@ -83,7 +88,7 @@ describe("sqs component", () => {
8388
mockListEventSourceMappingsPromise.mockResolvedValueOnce({
8489
EventSourceMappings: []
8590
});
86-
const component = new AwsSqsQueue("TestLambda", {
91+
const component = new AwsSqsQueue("TestSqs", {
8792
stateRoot: tmpStateFolder()
8893
});
8994
await component.init();
@@ -92,11 +97,47 @@ describe("sqs component", () => {
9297
});
9398

9499
it("calls the delete handler when component is deleted", async () => {
95-
const component = new AwsSqsQueue("TestLambda", {
100+
const component = new AwsSqsQueue("TestSqs", {
96101
stateRoot: tmpStateFolder()
97102
});
98103
await component.init();
99104
await component.remove();
100105
expect(mockDeleteQueuePromise).toBeCalledTimes(1);
101106
});
107+
108+
it("configures queue tags when tags are different", async () => {
109+
mockListQueueTagsPromise.mockResolvedValueOnce({
110+
Tags: {
111+
c: "d"
112+
}
113+
});
114+
115+
const component = new AwsSqsQueue("TestSqs", {
116+
stateRoot: tmpStateFolder()
117+
});
118+
await component.init();
119+
await component.default({ tags: { a: "b" } });
120+
121+
expect(mockListQueueTagsPromise).toBeCalledTimes(1);
122+
expect(mockTagQueuePromise).toBeCalledTimes(1);
123+
expect(mockUntagQueuePromise).toBeCalledTimes(1);
124+
});
125+
126+
it("does not configure queue tags when tags are the same", async () => {
127+
mockListQueueTagsPromise.mockResolvedValueOnce({
128+
Tags: {
129+
a: "b"
130+
}
131+
});
132+
133+
const component = new AwsSqsQueue("TestSqs", {
134+
stateRoot: tmpStateFolder()
135+
});
136+
await component.init();
137+
await component.default({ tags: { a: "b" } });
138+
139+
expect(mockListQueueTagsPromise).toBeCalledTimes(1);
140+
expect(mockTagQueuePromise).not.toBeCalled();
141+
expect(mockUntagQueuePromise).not.toBeCalled();
142+
});
102143
});

packages/serverless-components/aws-sqs/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
"url": "https://github.com/serverless-nextjs/serverless-next.js/issues"
1818
},
1919
"homepage": "https://github.com/serverless-nextjs/serverless-next.js#readme",
20+
"dependencies": {
21+
"@serverless/core": "^1.1.2",
22+
"lodash": "^4.17.21"
23+
},
2024
"peerDependencies": {
2125
"@serverless/core": "^1.1.2",
2226
"aws-sdk": "^2.920.0"

packages/serverless-components/aws-sqs/serverless.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,28 @@ const {
99
getAccountId,
1010
getArn,
1111
getUrl,
12-
setAttributes
12+
setAttributes,
13+
configureTags
1314
} = require("./utils");
1415

1516
const outputsList = ["arn", "url"];
1617

1718
const defaults = {
1819
name: "serverless",
19-
region: "us-east-1"
20+
region: "us-east-1",
21+
tags: undefined
2022
};
2123

2224
class AwsSqsQueue extends Component {
23-
async default(inputs = {}) {
25+
async default(
26+
inputs = {
27+
name: undefined,
28+
region: undefined,
29+
arn: undefined,
30+
url: undefined,
31+
tags: undefined
32+
}
33+
) {
2434
const config = mergeDeepRight(getDefaults({ defaults }), inputs);
2535
const accountId = await getAccountId(aws);
2636

@@ -86,10 +96,19 @@ class AwsSqsQueue extends Component {
8696
}
8797
}
8898

99+
// Synchronize tags if specified
100+
if (config.tags) {
101+
this.context.debug(
102+
"Configuring SQS queue tags since they are specified."
103+
);
104+
await configureTags(this.context, sqs, queueUrl, config.tags);
105+
}
106+
89107
this.state.name = config.name;
90108
this.state.arn = config.arn;
91109
this.state.url = config.url;
92110
this.state.region = config.region;
111+
this.state.tags = config.tags;
93112
await this.save();
94113

95114
const outputs = pick(outputsList, config);

packages/serverless-components/aws-sqs/utils.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { clone } = require("ramda");
2+
const _ = require("lodash");
23

34
const getDefaults = ({ defaults }) => {
45
const response = clone(defaults);
@@ -120,6 +121,39 @@ const deleteQueue = async ({ sqs, queueUrl }) => {
120121
}
121122
};
122123

124+
const configureTags = async (context, sqs, queueUrl, inputTags) => {
125+
const currentTags = {};
126+
127+
context.debug("Trying to get existing tags.");
128+
const data = await sqs.listQueueTags({ QueueUrl: queueUrl }).promise();
129+
130+
if (data.Tags) {
131+
Object.keys(data.Tags).forEach((key) => {
132+
currentTags[key] = data.Tags[key];
133+
});
134+
}
135+
136+
// Sync tags if different from current tags
137+
if (!_.isEqual(inputTags, currentTags)) {
138+
context.debug("Tags have changed. Updating tags.");
139+
await sqs
140+
.untagQueue({
141+
QueueUrl: queueUrl,
142+
TagKeys: Object.keys(inputTags)
143+
})
144+
.promise();
145+
146+
await sqs
147+
.tagQueue({
148+
QueueUrl: queueUrl,
149+
Tags: inputTags
150+
})
151+
.promise();
152+
} else {
153+
context.debug("Tags are the same as before, not doing anything.");
154+
}
155+
};
156+
123157
module.exports = {
124158
createQueue,
125159
deleteQueue,
@@ -129,5 +163,6 @@ module.exports = {
129163
getDefaults,
130164
getQueue,
131165
getAttributes,
132-
setAttributes
166+
setAttributes,
167+
configureTags
133168
};

0 commit comments

Comments
 (0)