@friggframework/serverless-plugin 2.0.0--canary.580.1003d8d.0 → 2.0.0--canary.580.4487187.0
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.
|
@@ -43,6 +43,19 @@ class LocalStackQueueService {
|
|
|
43
43
|
* Serialize a CloudFormation `Properties` object into the
|
|
44
44
|
* `Attributes` shape the SQS `CreateQueue` API accepts (string
|
|
45
45
|
* values only; object values like `RedrivePolicy` get JSON-encoded).
|
|
46
|
+
*
|
|
47
|
+
* Attributes whose value still contains an unresolved CloudFormation
|
|
48
|
+
* intrinsic (`Fn::GetAtt`, `Ref`, `Fn::Sub`, …) are DROPPED rather
|
|
49
|
+
* than stringified. Example: integration-builder.js emits
|
|
50
|
+
* `RedrivePolicy.deadLetterTargetArn: {'Fn::GetAtt': [...]}`, which
|
|
51
|
+
* CloudFormation resolves to a real ARN in AWS but is still a raw
|
|
52
|
+
* intrinsic object at local plugin-time. Forwarding that JSON blob
|
|
53
|
+
* to SQS `CreateQueue` would fail (`deadLetterTargetArn` must be a
|
|
54
|
+
* valid ARN string) or produce malformed config. Dropping the
|
|
55
|
+
* attribute gives local parity on every other queue property
|
|
56
|
+
* (notably `VisibilityTimeout`, which is the main reason this code
|
|
57
|
+
* exists) while leaving the DLQ association intentionally un-wired
|
|
58
|
+
* locally — matching the pre-PR behavior for that one attribute.
|
|
46
59
|
* @private
|
|
47
60
|
*/
|
|
48
61
|
_propertiesToAttributes(properties = {}) {
|
|
@@ -50,12 +63,46 @@ class LocalStackQueueService {
|
|
|
50
63
|
for (const key of LocalStackQueueService.PROPERTY_ATTRIBUTE_KEYS) {
|
|
51
64
|
const value = properties[key];
|
|
52
65
|
if (value === undefined || value === null) continue;
|
|
66
|
+
if (LocalStackQueueService._containsUnresolvedIntrinsic(value)) {
|
|
67
|
+
console.warn(
|
|
68
|
+
`[frigg-plugin] Skipping queue attribute "${key}" because it contains an unresolved CloudFormation intrinsic. ` +
|
|
69
|
+
`Deployed AWS will apply it via CloudFormation; local emulation will fall back to the AWS default for this attribute.`
|
|
70
|
+
);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
53
73
|
attributes[key] =
|
|
54
74
|
typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
55
75
|
}
|
|
56
76
|
return attributes;
|
|
57
77
|
}
|
|
58
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Recursively checks whether a value still contains a CloudFormation
|
|
81
|
+
* intrinsic function key (`Fn::*` or `Ref`). Such values are unsafe
|
|
82
|
+
* to pass through to SQS `CreateQueue` — AWS's runtime API doesn't
|
|
83
|
+
* understand CloudFormation intrinsics; they're only valid inside
|
|
84
|
+
* serverless.yml / CloudFormation templates.
|
|
85
|
+
* @private
|
|
86
|
+
*/
|
|
87
|
+
static _containsUnresolvedIntrinsic(value) {
|
|
88
|
+
if (value === null || value === undefined) return false;
|
|
89
|
+
if (typeof value !== 'object') return false;
|
|
90
|
+
if (Array.isArray(value)) {
|
|
91
|
+
return value.some((v) =>
|
|
92
|
+
LocalStackQueueService._containsUnresolvedIntrinsic(v)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
for (const key of Object.keys(value)) {
|
|
96
|
+
if (key === 'Ref' || key.startsWith('Fn::')) return true;
|
|
97
|
+
if (
|
|
98
|
+
LocalStackQueueService._containsUnresolvedIntrinsic(value[key])
|
|
99
|
+
) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
59
106
|
/**
|
|
60
107
|
* @param {string} queueName - Name of queue to create
|
|
61
108
|
* @param {Object} [attributes] - SQS CreateQueue Attributes (already
|
|
@@ -109,6 +109,68 @@ describe('LocalStackQueueService', () => {
|
|
|
109
109
|
it('returns an empty object when properties are missing', () => {
|
|
110
110
|
expect(service._propertiesToAttributes()).toEqual({});
|
|
111
111
|
});
|
|
112
|
+
|
|
113
|
+
it('drops attributes containing unresolved CloudFormation intrinsics', () => {
|
|
114
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
115
|
+
const attrs = service._propertiesToAttributes({
|
|
116
|
+
VisibilityTimeout: 1800,
|
|
117
|
+
RedrivePolicy: {
|
|
118
|
+
maxReceiveCount: 3,
|
|
119
|
+
deadLetterTargetArn: {
|
|
120
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// VisibilityTimeout survives; RedrivePolicy is dropped because
|
|
126
|
+
// deadLetterTargetArn is still an unresolved Fn::GetAtt intrinsic
|
|
127
|
+
// (real AWS resolves it via CloudFormation; LocalStack cannot).
|
|
128
|
+
expect(attrs).toEqual({ VisibilityTimeout: '1800' });
|
|
129
|
+
expect(attrs).not.toHaveProperty('RedrivePolicy');
|
|
130
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
131
|
+
expect.stringContaining('Skipping queue attribute "RedrivePolicy"')
|
|
132
|
+
);
|
|
133
|
+
warnSpy.mockRestore();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('drops attributes with Ref intrinsics', () => {
|
|
137
|
+
jest.spyOn(console, 'warn').mockImplementation();
|
|
138
|
+
const attrs = service._propertiesToAttributes({
|
|
139
|
+
VisibilityTimeout: 60,
|
|
140
|
+
KmsMasterKeyId: { Ref: 'MyKmsKey' },
|
|
141
|
+
});
|
|
142
|
+
expect(attrs).toEqual({ VisibilityTimeout: '60' });
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('detects intrinsics nested deep inside objects and arrays', () => {
|
|
146
|
+
expect(
|
|
147
|
+
LocalStackQueueService._containsUnresolvedIntrinsic({
|
|
148
|
+
a: { b: [{ c: { 'Fn::Sub': '${AWS::Region}' } }] },
|
|
149
|
+
})
|
|
150
|
+
).toBe(true);
|
|
151
|
+
expect(
|
|
152
|
+
LocalStackQueueService._containsUnresolvedIntrinsic({
|
|
153
|
+
a: { b: [{ c: 'hello' }] },
|
|
154
|
+
})
|
|
155
|
+
).toBe(false);
|
|
156
|
+
expect(LocalStackQueueService._containsUnresolvedIntrinsic(null)).toBe(false);
|
|
157
|
+
expect(LocalStackQueueService._containsUnresolvedIntrinsic('str')).toBe(false);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('retains resolved RedrivePolicy (ARN already a string)', () => {
|
|
161
|
+
const attrs = service._propertiesToAttributes({
|
|
162
|
+
RedrivePolicy: {
|
|
163
|
+
maxReceiveCount: 3,
|
|
164
|
+
deadLetterTargetArn: 'arn:aws:sqs:us-east-1:x:dlq',
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
expect(attrs.RedrivePolicy).toBe(
|
|
168
|
+
JSON.stringify({
|
|
169
|
+
maxReceiveCount: 3,
|
|
170
|
+
deadLetterTargetArn: 'arn:aws:sqs:us-east-1:x:dlq',
|
|
171
|
+
})
|
|
172
|
+
);
|
|
173
|
+
});
|
|
112
174
|
});
|
|
113
175
|
|
|
114
176
|
describe('createQueues', () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/serverless-plugin",
|
|
3
|
-
"version": "2.0.0--canary.580.
|
|
3
|
+
"version": "2.0.0--canary.580.4487187.0",
|
|
4
4
|
"description": "Plugin to help dynamically load frigg resources",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
14
|
-
"gitHead": "
|
|
14
|
+
"gitHead": "44871876975b9e507ed635e9df4075b2215685d1"
|
|
15
15
|
}
|