@comicrelief/lambda-wrapper 1.10.2 → 2.0.0-beta.10
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.
- package/README.md +196 -50
- package/dist/core/DependencyAwareClass.d.ts +16 -0
- package/dist/core/DependencyAwareClass.d.ts.map +1 -0
- package/dist/core/DependencyAwareClass.js +20 -0
- package/dist/core/DependencyAwareClass.js.map +1 -0
- package/dist/core/DependencyInjection.d.ts +62 -0
- package/dist/core/DependencyInjection.d.ts.map +1 -0
- package/dist/core/DependencyInjection.js +105 -0
- package/dist/core/DependencyInjection.js.map +1 -0
- package/dist/core/LambdaWrapper.d.ts +73 -0
- package/dist/core/LambdaWrapper.d.ts.map +1 -0
- package/dist/core/LambdaWrapper.js +170 -0
- package/dist/core/LambdaWrapper.js.map +1 -0
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +21 -0
- package/dist/core/config.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -143
- package/dist/index.js.map +1 -0
- package/dist/models/ResponseModel.d.ts +73 -0
- package/dist/models/ResponseModel.d.ts.map +1 -0
- package/dist/models/ResponseModel.js +100 -0
- package/dist/models/ResponseModel.js.map +1 -0
- package/dist/models/SQSMessageModel.d.ts +46 -0
- package/dist/models/SQSMessageModel.d.ts.map +1 -0
- package/dist/models/SQSMessageModel.js +65 -0
- package/dist/models/SQSMessageModel.js.map +1 -0
- package/dist/services/BaseConfigService.d.ts +101 -0
- package/dist/services/BaseConfigService.d.ts.map +1 -0
- package/dist/services/BaseConfigService.js +180 -0
- package/dist/services/BaseConfigService.js.map +1 -0
- package/dist/services/HTTPService.d.ts +29 -0
- package/dist/services/HTTPService.d.ts.map +1 -0
- package/dist/services/HTTPService.js +55 -0
- package/dist/services/HTTPService.js.map +1 -0
- package/dist/services/LoggerService.d.ts +102 -0
- package/dist/services/LoggerService.d.ts.map +1 -0
- package/dist/services/LoggerService.js +249 -0
- package/dist/services/LoggerService.js.map +1 -0
- package/dist/services/RequestService.d.ts +119 -0
- package/dist/services/RequestService.d.ts.map +1 -0
- package/dist/services/RequestService.js +293 -0
- package/dist/services/RequestService.js.map +1 -0
- package/dist/services/SQSService.d.ts +263 -0
- package/dist/services/SQSService.d.ts.map +1 -0
- package/dist/services/SQSService.js +411 -0
- package/dist/services/SQSService.js.map +1 -0
- package/dist/services/TimerService.d.ts +22 -0
- package/dist/services/TimerService.d.ts.map +1 -0
- package/dist/services/TimerService.js +40 -0
- package/dist/services/TimerService.js.map +1 -0
- package/dist/types/Status.d.ts +6 -0
- package/dist/types/Status.d.ts.map +1 -0
- package/dist/types/Status.js +3 -0
- package/dist/types/Status.js.map +1 -0
- package/dist/utils/LambdaTermination.d.ts +14 -0
- package/dist/utils/LambdaTermination.d.ts.map +1 -0
- package/dist/utils/LambdaTermination.js +23 -0
- package/dist/utils/LambdaTermination.js.map +1 -0
- package/dist/utils/PromisifiedDelay.d.ts +17 -0
- package/dist/utils/PromisifiedDelay.d.ts.map +1 -0
- package/dist/utils/PromisifiedDelay.js +48 -0
- package/dist/utils/PromisifiedDelay.js.map +1 -0
- package/package.json +36 -26
- package/.eslintrc.yml +0 -16
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/main.yml +0 -74
- package/babel.config.js +0 -13
- package/dist/Config/Dependencies.js +0 -33
- package/dist/DependencyInjection/DependencyAware.class.js +0 -40
- package/dist/DependencyInjection/DependencyInjection.class.js +0 -115
- package/dist/Model/CloudEvent.model.js +0 -138
- package/dist/Model/Model.model.js +0 -39
- package/dist/Model/Response.model.js +0 -135
- package/dist/Model/SQS/MarketingPreference.constraints.json +0 -28
- package/dist/Model/SQS/MarketingPreference.model.js +0 -583
- package/dist/Model/SQS/Message.model.js +0 -92
- package/dist/Model/Status.model.js +0 -71
- package/dist/Service/BaseConfig.service.js +0 -204
- package/dist/Service/HTTP.service.js +0 -57
- package/dist/Service/Logger.service.js +0 -256
- package/dist/Service/Request.service.js +0 -294
- package/dist/Service/SQS.service.js +0 -367
- package/dist/Service/Timer.service.js +0 -46
- package/dist/Wrapper/LambdaTermination.js +0 -34
- package/dist/Wrapper/LambdaWrapper.js +0 -143
- package/dist/Wrapper/PromisifiedDelay.js +0 -55
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = exports.REQUEST_TYPES = exports.HTTP_METHODS_WITH_PAYLOADS = exports.HTTP_METHODS_WITHOUT_PAYLOADS = exports.ERROR_TYPES = void 0;
|
|
7
|
-
var _querystring = _interopRequireDefault(require("querystring"));
|
|
8
|
-
var _useragent = _interopRequireDefault(require("useragent"));
|
|
9
|
-
var _validate = _interopRequireDefault(require("validate.js/validate"));
|
|
10
|
-
var _xml2js = _interopRequireDefault(require("xml2js"));
|
|
11
|
-
var _Dependencies = require("../Config/Dependencies");
|
|
12
|
-
var _DependencyAware = _interopRequireDefault(require("../DependencyInjection/DependencyAware.class"));
|
|
13
|
-
var _Response = _interopRequireDefault(require("../Model/Response.model"));
|
|
14
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
-
/* eslint-disable class-methods-use-this */
|
|
16
|
-
/* eslint-disable sonarjs/no-duplicate-string */const REQUEST_TYPES = {
|
|
17
|
-
DELETE: 'DELETE',
|
|
18
|
-
GET: 'GET',
|
|
19
|
-
HEAD: 'HEAD',
|
|
20
|
-
OPTIONS: 'OPTIONS',
|
|
21
|
-
PATCH: 'PATCH',
|
|
22
|
-
POST: 'POST',
|
|
23
|
-
PUT: 'PUT'
|
|
24
|
-
};
|
|
25
|
-
exports.REQUEST_TYPES = REQUEST_TYPES;
|
|
26
|
-
const HTTP_METHODS_WITHOUT_PAYLOADS = [REQUEST_TYPES.DELETE, REQUEST_TYPES.GET, REQUEST_TYPES.HEAD, REQUEST_TYPES.OPTIONS];
|
|
27
|
-
exports.HTTP_METHODS_WITHOUT_PAYLOADS = HTTP_METHODS_WITHOUT_PAYLOADS;
|
|
28
|
-
const HTTP_METHODS_WITH_PAYLOADS = [REQUEST_TYPES.PATCH, REQUEST_TYPES.POST, REQUEST_TYPES.PUT];
|
|
29
|
-
|
|
30
|
-
// Define action specific error types
|
|
31
|
-
exports.HTTP_METHODS_WITH_PAYLOADS = HTTP_METHODS_WITH_PAYLOADS;
|
|
32
|
-
const ERROR_TYPES = {
|
|
33
|
-
VALIDATION_ERROR: new _Response.default({}, 400, 'required fields are missing')
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* RequestService class
|
|
38
|
-
*/
|
|
39
|
-
exports.ERROR_TYPES = ERROR_TYPES;
|
|
40
|
-
class RequestService extends _DependencyAware.default {
|
|
41
|
-
/**
|
|
42
|
-
* Get a parameter from the request.
|
|
43
|
-
*
|
|
44
|
-
* @param parameter
|
|
45
|
-
* @param ifNull
|
|
46
|
-
* @param requestType
|
|
47
|
-
*/
|
|
48
|
-
get(parameter, ifNull = null, requestType = null) {
|
|
49
|
-
const queryParameters = this.getAll(requestType);
|
|
50
|
-
if (queryParameters === null) {
|
|
51
|
-
return ifNull;
|
|
52
|
-
}
|
|
53
|
-
return typeof queryParameters[parameter] !== 'undefined' ? queryParameters[parameter] : ifNull;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get all HTTP headers included in the request.
|
|
58
|
-
*
|
|
59
|
-
* @returns {object} An object with a key for each header.
|
|
60
|
-
*/
|
|
61
|
-
getAllHeaders() {
|
|
62
|
-
return {
|
|
63
|
-
...this.getContainer().getEvent().headers
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Get an HTTP header from the request.
|
|
69
|
-
*
|
|
70
|
-
* The header name is case-insensitive.
|
|
71
|
-
*
|
|
72
|
-
* @param {string} name The name of the header.
|
|
73
|
-
* @param {string} [whenMissing] Value to return if the header is missing.
|
|
74
|
-
* (default: empty string)
|
|
75
|
-
* @returns {string}
|
|
76
|
-
*/
|
|
77
|
-
getHeader(name, whenMissing = '') {
|
|
78
|
-
const headers = this.getAllHeaders();
|
|
79
|
-
if (!headers) {
|
|
80
|
-
return whenMissing;
|
|
81
|
-
}
|
|
82
|
-
const lowerName = name.toLowerCase();
|
|
83
|
-
const key = Object.keys(headers).find(k => k.toLowerCase() === lowerName);
|
|
84
|
-
return key && headers[key] || whenMissing;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Get authorization token
|
|
89
|
-
*
|
|
90
|
-
* @returns {*}
|
|
91
|
-
*/
|
|
92
|
-
getAuthorizationToken() {
|
|
93
|
-
const authorization = this.getHeader('Authorization');
|
|
94
|
-
if (!authorization) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
const tokenParts = authorization.split(' ');
|
|
98
|
-
const tokenValue = tokenParts[1];
|
|
99
|
-
if (!(tokenParts[0].toLowerCase() === 'bearer' && tokenValue)) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
return tokenValue;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Get a path parameter
|
|
107
|
-
*
|
|
108
|
-
* @param parameter
|
|
109
|
-
* @param ifNull mixed
|
|
110
|
-
*/
|
|
111
|
-
getPathParameter(parameter = null, ifNull = {}) {
|
|
112
|
-
const event = this.getContainer().getEvent();
|
|
113
|
-
|
|
114
|
-
// If no parameter has been requested, return all path parameters
|
|
115
|
-
if (parameter === null && typeof event.pathParameters === 'object') {
|
|
116
|
-
return event.pathParameters;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// If a specifc parameter has been requested, return the parameter if it exists
|
|
120
|
-
if (typeof parameter === 'string' && typeof event.pathParameters === 'object' && event.pathParameters !== null && typeof event.pathParameters[parameter] !== 'undefined') {
|
|
121
|
-
return event.pathParameters[parameter];
|
|
122
|
-
}
|
|
123
|
-
return ifNull;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Get all request parameters
|
|
128
|
-
*
|
|
129
|
-
* @param requestType
|
|
130
|
-
* @returns {{}}
|
|
131
|
-
*/
|
|
132
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
133
|
-
getAll(requestType = null) {
|
|
134
|
-
const event = this.getContainer().getEvent();
|
|
135
|
-
if (HTTP_METHODS_WITHOUT_PAYLOADS.includes(event.httpMethod) || HTTP_METHODS_WITHOUT_PAYLOADS.includes(requestType)) {
|
|
136
|
-
// get simple parameters
|
|
137
|
-
const params = {
|
|
138
|
-
...event.queryStringParameters
|
|
139
|
-
};
|
|
140
|
-
// add array parameters as arrays
|
|
141
|
-
Object.keys(params).filter(key => key.endsWith('[]')).forEach(key => {
|
|
142
|
-
params[key] = event.multiValueQueryStringParameters[key];
|
|
143
|
-
});
|
|
144
|
-
return params;
|
|
145
|
-
}
|
|
146
|
-
if (HTTP_METHODS_WITH_PAYLOADS.includes(event.httpMethod) || HTTP_METHODS_WITH_PAYLOADS.includes(requestType)) {
|
|
147
|
-
const contentType = this.getHeader('Content-Type');
|
|
148
|
-
let queryParameters = {};
|
|
149
|
-
if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
150
|
-
queryParameters = _querystring.default.parse(event.body);
|
|
151
|
-
}
|
|
152
|
-
if (contentType.includes('application/json')) {
|
|
153
|
-
try {
|
|
154
|
-
queryParameters = JSON.parse(event.body);
|
|
155
|
-
} catch {
|
|
156
|
-
queryParameters = {};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (contentType.includes('text/xml')) {
|
|
160
|
-
_xml2js.default.parseString(event.body, (error, result) => {
|
|
161
|
-
queryParameters = error ? {} : result;
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
if (contentType.includes('multipart/form-data')) {
|
|
165
|
-
queryParameters = this.parseForm(true);
|
|
166
|
-
}
|
|
167
|
-
return typeof queryParameters !== 'undefined' ? queryParameters : {};
|
|
168
|
-
}
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Fetch the request IP address
|
|
174
|
-
*
|
|
175
|
-
* @returns {*}
|
|
176
|
-
*/
|
|
177
|
-
getIp() {
|
|
178
|
-
const event = this.getContainer().getEvent();
|
|
179
|
-
if (typeof event.requestContext !== 'undefined' && typeof event.requestContext.identity !== 'undefined' && typeof event.requestContext.identity.sourceIp !== 'undefined') {
|
|
180
|
-
return event.requestContext.identity.sourceIp;
|
|
181
|
-
}
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Get user agent
|
|
187
|
-
*
|
|
188
|
-
* @returns {*}
|
|
189
|
-
*/
|
|
190
|
-
getUserBrowserAndDevice() {
|
|
191
|
-
const userAgent = this.getHeader('user-agent', null);
|
|
192
|
-
if (userAgent === null) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
try {
|
|
196
|
-
const agent = _useragent.default.parse(userAgent);
|
|
197
|
-
const os = agent.os.toJSON();
|
|
198
|
-
return {
|
|
199
|
-
'browser-type': agent.family,
|
|
200
|
-
'browser-version': agent.toVersion(),
|
|
201
|
-
'device-type': agent.device.family,
|
|
202
|
-
'operating-system': os.family,
|
|
203
|
-
'operating-system-version': agent.os.toVersion()
|
|
204
|
-
};
|
|
205
|
-
} catch {
|
|
206
|
-
this.getContainer().get(_Dependencies.DEFINITIONS.LOGGER).label('user-agent-parsing-failed');
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Test a request against validation constraints
|
|
213
|
-
*
|
|
214
|
-
* @param constraints
|
|
215
|
-
* @returns {Promise<any>}
|
|
216
|
-
*/
|
|
217
|
-
validateAgainstConstraints(constraints) {
|
|
218
|
-
const Logger = this.getContainer().get(_Dependencies.DEFINITIONS.LOGGER);
|
|
219
|
-
return new Promise((resolve, reject) => {
|
|
220
|
-
const validation = (0, _validate.default)(this.getAll(), constraints);
|
|
221
|
-
if (typeof validation === 'undefined') {
|
|
222
|
-
resolve();
|
|
223
|
-
} else {
|
|
224
|
-
Logger.label('request-validation-failed');
|
|
225
|
-
const validationErrorResponse = ERROR_TYPES.VALIDATION_ERROR;
|
|
226
|
-
validationErrorResponse.setBodyVariable('validation_errors', validation);
|
|
227
|
-
reject(validationErrorResponse);
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Fetch the request multipart form
|
|
234
|
-
*
|
|
235
|
-
* @param useBuffer
|
|
236
|
-
* @returns {*}
|
|
237
|
-
*/
|
|
238
|
-
parseForm(useBuffer) {
|
|
239
|
-
const event = this.getContainer().getEvent();
|
|
240
|
-
const boundary = this.getBoundary(event);
|
|
241
|
-
const body = event.isBase64Encoded ? Buffer.from(event.body, 'base64').toString('binary').trim() : event.body;
|
|
242
|
-
const result = {};
|
|
243
|
-
body.split(boundary).forEach(item => {
|
|
244
|
-
if (/filename=".+"/g.test(item)) {
|
|
245
|
-
result[item.match(/name=".+";/g)[0].slice(6, -2)] = {
|
|
246
|
-
type: 'file',
|
|
247
|
-
filename: item.match(/filename=".+"/g)[0].slice(10, -1),
|
|
248
|
-
contentType: item.match(/Content-Type:\s.+/g)[0].slice(14),
|
|
249
|
-
content: useBuffer ? Buffer.from(item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4), 'binary') : item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4)
|
|
250
|
-
};
|
|
251
|
-
} else if (/name=".+"/g.test(item)) {
|
|
252
|
-
result[item.match(/name=".+"/g)[0].slice(6, -1)] = item.slice(item.search(/name=".+"/g) + item.match(/name=".+"/g)[0].length + 4, -4);
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
return result;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Fetch the request AWS event Records
|
|
260
|
-
*
|
|
261
|
-
* @returns {*}
|
|
262
|
-
*/
|
|
263
|
-
getAWSRecords() {
|
|
264
|
-
const event = this.getContainer().getEvent();
|
|
265
|
-
const eventRecord = event.Records && event.Records[0];
|
|
266
|
-
if (typeof event.Records !== 'undefined' && typeof event.Records[0] !== 'undefined' && typeof eventRecord.eventSource !== 'undefined') {
|
|
267
|
-
return eventRecord;
|
|
268
|
-
}
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Gets a value independently from
|
|
274
|
-
* the case of the key
|
|
275
|
-
*
|
|
276
|
-
* @param object
|
|
277
|
-
* @param key
|
|
278
|
-
*/
|
|
279
|
-
getValueIgnoringKeyCase(object, key) {
|
|
280
|
-
const foundKey = Object.keys(object).find(currentKey => currentKey.toLocaleLowerCase() === key.toLowerCase());
|
|
281
|
-
return object[foundKey];
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Returns the content type
|
|
286
|
-
* assoiated with the request
|
|
287
|
-
*
|
|
288
|
-
* @param event
|
|
289
|
-
*/
|
|
290
|
-
getBoundary(event) {
|
|
291
|
-
return this.getValueIgnoringKeyCase(event.headers, 'Content-Type').split('=')[1];
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
exports.default = RequestService;
|
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = exports.SQS_PUBLISH_FAILURE_MODES = exports.SQS_OFFLINE_MODES = void 0;
|
|
7
|
-
var _alai = _interopRequireDefault(require("alai"));
|
|
8
|
-
var _each = _interopRequireDefault(require("async/each"));
|
|
9
|
-
var _awsSdk = _interopRequireDefault(require("aws-sdk"));
|
|
10
|
-
var _uuid = require("uuid");
|
|
11
|
-
var _Dependencies = require("../Config/Dependencies");
|
|
12
|
-
var _DependencyAware = _interopRequireDefault(require("../DependencyInjection/DependencyAware.class"));
|
|
13
|
-
var _DependencyInjection = _interopRequireDefault(require("../DependencyInjection/DependencyInjection.class"));
|
|
14
|
-
var _Message = _interopRequireDefault(require("../Model/SQS/Message.model"));
|
|
15
|
-
var _Status = _interopRequireWildcard(require("../Model/Status.model"));
|
|
16
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
17
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
18
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
|
-
/**
|
|
20
|
-
* Allowed values for `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_MODE`.
|
|
21
|
-
*/
|
|
22
|
-
const SQS_OFFLINE_MODES = {
|
|
23
|
-
/**
|
|
24
|
-
* When running offline, messages will trigger the consumer function directly
|
|
25
|
-
* via a Lambda endpoint, set using `process.env.SERVICE_LAMBDA_URL`. This is
|
|
26
|
-
* the default.
|
|
27
|
-
*/
|
|
28
|
-
DIRECT: 'direct',
|
|
29
|
-
/**
|
|
30
|
-
* When running offline, send messages to an offline SQS service defined by
|
|
31
|
-
* `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_HOST`.
|
|
32
|
-
*/
|
|
33
|
-
LOCAL: 'local',
|
|
34
|
-
/**
|
|
35
|
-
* When running offline, send messages to AWS as normal.
|
|
36
|
-
*/
|
|
37
|
-
AWS: 'aws'
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Defines the preferred behaviour
|
|
42
|
-
* for SQSService.prototype.publish
|
|
43
|
-
* should AWS SQS fail.
|
|
44
|
-
*/
|
|
45
|
-
exports.SQS_OFFLINE_MODES = SQS_OFFLINE_MODES;
|
|
46
|
-
const SQS_PUBLISH_FAILURE_MODES = {
|
|
47
|
-
/**
|
|
48
|
-
* Catches the exception and logs it.
|
|
49
|
-
* This is the default behaviour
|
|
50
|
-
* for LambdaWrapper 1.8.0 and below
|
|
51
|
-
* and for LambdaWrapper 1.8.2 and above
|
|
52
|
-
*/
|
|
53
|
-
CATCH: 'catch',
|
|
54
|
-
/**
|
|
55
|
-
* Throws the exception so that the caller
|
|
56
|
-
* can handle it directly.
|
|
57
|
-
*/
|
|
58
|
-
THROW: 'throw'
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* SQSService class
|
|
63
|
-
*/
|
|
64
|
-
exports.SQS_PUBLISH_FAILURE_MODES = SQS_PUBLISH_FAILURE_MODES;
|
|
65
|
-
class SQSService extends _DependencyAware.default {
|
|
66
|
-
/**
|
|
67
|
-
* SQSService constructor
|
|
68
|
-
*
|
|
69
|
-
* @param di DependencyInjection
|
|
70
|
-
*/
|
|
71
|
-
constructor(di) {
|
|
72
|
-
super(di);
|
|
73
|
-
const {
|
|
74
|
-
LAMBDA_WRAPPER_OFFLINE_SQS_HOST: offlineHost = 'localhost',
|
|
75
|
-
LAMBDA_WRAPPER_OFFLINE_SQS_PORT: offlinePort = '4576',
|
|
76
|
-
LAMBDA_WRAPPER_OFFLINE_SQS_MODE: offlineMode = SQS_OFFLINE_MODES.DIRECT,
|
|
77
|
-
AWS_ACCOUNT_ID,
|
|
78
|
-
REGION
|
|
79
|
-
} = process.env;
|
|
80
|
-
const container = this.getContainer();
|
|
81
|
-
const context = container.getContext();
|
|
82
|
-
const queues = container.getConfiguration('QUEUES');
|
|
83
|
-
const accountId = context && context.invokedFunctionArn && _alai.default.parse(context) || AWS_ACCOUNT_ID;
|
|
84
|
-
this.queues = {};
|
|
85
|
-
this.$lambda = null;
|
|
86
|
-
this.$sqs = null;
|
|
87
|
-
if (container.isOffline && !Object.values(SQS_OFFLINE_MODES).includes(offlineMode)) {
|
|
88
|
-
throw new Error(`Invalid LAMBDA_WRAPPER_OFFLINE_SQS_MODE: ${offlineMode}\n` + `Please use one of: ${Object.values(SQS_OFFLINE_MODES).join(', ')}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Add the queues from configuration
|
|
92
|
-
if (queues !== null && Object.keys(queues).length > 0) {
|
|
93
|
-
Object.keys(queues).forEach(queueDefinition => {
|
|
94
|
-
if (container.isOffline && offlineMode === SQS_OFFLINE_MODES.LOCAL) {
|
|
95
|
-
// custom URL when using an offline SQS service such as Localstack
|
|
96
|
-
this.queues[queueDefinition] = `http://${offlineHost}:${offlinePort}/queue/${queues[queueDefinition]}`;
|
|
97
|
-
} else {
|
|
98
|
-
// default AWS queue URL
|
|
99
|
-
this.queues[queueDefinition] = `https://sqs.${REGION}.amazonaws.com/${accountId}/${queues[queueDefinition]}`;
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Returns an SQS client instance
|
|
107
|
-
*/
|
|
108
|
-
get sqs() {
|
|
109
|
-
if (!this.$sqs) {
|
|
110
|
-
this.$sqs = new _awsSdk.default.SQS({
|
|
111
|
-
region: process.env.REGION,
|
|
112
|
-
httpOptions: {
|
|
113
|
-
// longest publish on NOTV took 5 seconds
|
|
114
|
-
connectTimeout: 8 * 1000,
|
|
115
|
-
timeout: 8 * 1000
|
|
116
|
-
},
|
|
117
|
-
maxRetries: 3 // default is 3, we can change that
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return this.$sqs;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Returns a Lambda client instance
|
|
126
|
-
*/
|
|
127
|
-
get lambda() {
|
|
128
|
-
if (!this.$lambda) {
|
|
129
|
-
const endpoint = process.env.SERVICE_LAMBDA_URL;
|
|
130
|
-
if (!endpoint) {
|
|
131
|
-
throw new Error('process.env.SERVICE_LAMBDA_URL must be defined.');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// move to subprocess
|
|
135
|
-
this.$lambda = new _awsSdk.default.Lambda({
|
|
136
|
-
region: process.env.AWS_REGION,
|
|
137
|
-
endpoint
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
return this.$lambda;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Returns the mode to use for offline SQS.
|
|
145
|
-
*
|
|
146
|
-
* This is configured by `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_MODE`. The
|
|
147
|
-
* default is `SQS_OFFLINE_MODES.LAMBDA`.
|
|
148
|
-
*/
|
|
149
|
-
static get offlineMode() {
|
|
150
|
-
return process.env.LAMBDA_WRAPPER_OFFLINE_SQS_MODE || SQS_OFFLINE_MODES.DIRECT;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Batch delete messages
|
|
155
|
-
*
|
|
156
|
-
* @param queue strung
|
|
157
|
-
* @param messageModels [SQSMessageModel]
|
|
158
|
-
* @returns {Promise<any>}
|
|
159
|
-
*/
|
|
160
|
-
batchDelete(queue, messageModels) {
|
|
161
|
-
const container = this.getContainer();
|
|
162
|
-
const queueUrl = this.queues[queue];
|
|
163
|
-
const Logger = container.get(_Dependencies.DEFINITIONS.LOGGER);
|
|
164
|
-
const Timer = container.get(_Dependencies.DEFINITIONS.TIMER);
|
|
165
|
-
const timerId = `sqs-batch-delete-${(0, _uuid.v4)()} - Queue: '${queueUrl}'`;
|
|
166
|
-
return new Promise(resolve => {
|
|
167
|
-
const messagesForDeletion = [];
|
|
168
|
-
Timer.start(timerId);
|
|
169
|
-
// assuming openFiles is an array of file names
|
|
170
|
-
(0, _each.default)(messageModels, (messageModel, callback) => {
|
|
171
|
-
if (messageModel instanceof _Message.default && messageModel.isForDeletion() === true) {
|
|
172
|
-
messagesForDeletion.push({
|
|
173
|
-
Id: messageModel.getMessageId(),
|
|
174
|
-
ReceiptHandle: messageModel.getReceiptHandle()
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
callback();
|
|
178
|
-
}, loopError => {
|
|
179
|
-
if (loopError) {
|
|
180
|
-
Logger.error(loopError);
|
|
181
|
-
resolve();
|
|
182
|
-
}
|
|
183
|
-
this.sqs.deleteMessageBatch({
|
|
184
|
-
Entries: messagesForDeletion,
|
|
185
|
-
QueueUrl: queueUrl
|
|
186
|
-
}, error => {
|
|
187
|
-
Timer.stop(timerId);
|
|
188
|
-
if (error) {
|
|
189
|
-
Logger.error(error);
|
|
190
|
-
}
|
|
191
|
-
resolve();
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Check SQS status
|
|
199
|
-
*
|
|
200
|
-
* @returns {Promise<any>}
|
|
201
|
-
*/
|
|
202
|
-
checkStatus() {
|
|
203
|
-
const container = this.getContainer();
|
|
204
|
-
const Logger = container.get(_Dependencies.DEFINITIONS.LOGGER);
|
|
205
|
-
const Timer = container.get(_Dependencies.DEFINITIONS.TIMER);
|
|
206
|
-
const timerId = `sqs-list-queues-${(0, _uuid.v4)()}`;
|
|
207
|
-
return new Promise(resolve => {
|
|
208
|
-
Timer.start(timerId);
|
|
209
|
-
this.sqs.listQueues({}, (error, data) => {
|
|
210
|
-
Timer.stop(timerId);
|
|
211
|
-
const statusModel = new _Status.default('SQS', _Status.STATUS_TYPES.OK);
|
|
212
|
-
if (error) {
|
|
213
|
-
Logger.error(error);
|
|
214
|
-
statusModel.setStatus(_Status.STATUS_TYPES.APPLICATION_FAILURE);
|
|
215
|
-
}
|
|
216
|
-
if (typeof data.QueueUrls === 'undefined' || data.QueueUrls.length === 0) {
|
|
217
|
-
statusModel.setStatus(_Status.STATUS_TYPES.APPLICATION_FAILURE);
|
|
218
|
-
}
|
|
219
|
-
resolve(statusModel);
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Get number of messages in a queue
|
|
226
|
-
*
|
|
227
|
-
* @param queue
|
|
228
|
-
* @returns {Promise<any>}
|
|
229
|
-
*/
|
|
230
|
-
getMessageCount(queue) {
|
|
231
|
-
const container = this.getContainer();
|
|
232
|
-
const queueUrl = this.queues[queue];
|
|
233
|
-
const Logger = container.get(_Dependencies.DEFINITIONS.LOGGER);
|
|
234
|
-
const Timer = container.get(_Dependencies.DEFINITIONS.TIMER);
|
|
235
|
-
const timerId = `sqs-get-queue-attributes-${(0, _uuid.v4)()} - Queue: '${queueUrl}'`;
|
|
236
|
-
return new Promise(resolve => {
|
|
237
|
-
Timer.start(timerId);
|
|
238
|
-
this.sqs.getQueueAttributes({
|
|
239
|
-
AttributeNames: ['ApproximateNumberOfMessages'],
|
|
240
|
-
QueueUrl: queueUrl
|
|
241
|
-
}, (error, data) => {
|
|
242
|
-
Timer.stop(timerId);
|
|
243
|
-
if (error) {
|
|
244
|
-
Logger.error(error);
|
|
245
|
-
resolve(0);
|
|
246
|
-
}
|
|
247
|
-
resolve(Number.parseInt(data.Attributes.ApproximateNumberOfMessages, 10));
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Publish to message queue
|
|
254
|
-
*
|
|
255
|
-
* When running within serverless-offline, messages can be published to a
|
|
256
|
-
* local Lambda or SQS service instead of to AWS, depending on the offline
|
|
257
|
-
* mode specified by `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_MODE`.
|
|
258
|
-
*
|
|
259
|
-
* @param queue string
|
|
260
|
-
* @param messageObject object
|
|
261
|
-
* @param messageGroupId string
|
|
262
|
-
* @param {'catch' | 'throw'} failureMode Choose how failures are handled:
|
|
263
|
-
* - `catch`: errors will be caught and logged. This is the default.
|
|
264
|
-
* - `throw`: errors will be thrown, causing promise to reject.
|
|
265
|
-
* @returns {Promise<any>}
|
|
266
|
-
*/
|
|
267
|
-
async publish(queue, messageObject, messageGroupId = null, failureMode = SQS_PUBLISH_FAILURE_MODES.CATCH) {
|
|
268
|
-
if (!Object.values(SQS_PUBLISH_FAILURE_MODES).includes(failureMode)) {
|
|
269
|
-
throw new Error(`Invalid value for 'failureMode': ${failureMode}`);
|
|
270
|
-
}
|
|
271
|
-
const container = this.getContainer();
|
|
272
|
-
const queueUrl = this.queues[queue];
|
|
273
|
-
const Timer = container.get(_Dependencies.DEFINITIONS.TIMER);
|
|
274
|
-
const timerId = `sqs-send-message-${(0, _uuid.v4)()} - Queue: '${queueUrl}'`;
|
|
275
|
-
Timer.start(timerId);
|
|
276
|
-
const messageParameters = {
|
|
277
|
-
MessageBody: JSON.stringify(messageObject),
|
|
278
|
-
QueueUrl: queueUrl
|
|
279
|
-
};
|
|
280
|
-
if (queueUrl.includes('.fifo')) {
|
|
281
|
-
messageParameters.MessageDeduplicationId = (0, _uuid.v4)();
|
|
282
|
-
messageParameters.MessageGroupId = messageGroupId !== null ? messageGroupId : (0, _uuid.v4)();
|
|
283
|
-
}
|
|
284
|
-
try {
|
|
285
|
-
if (container.isOffline && this.constructor.offlineMode === SQS_OFFLINE_MODES.DIRECT) {
|
|
286
|
-
await this.publishOffline(queue, messageParameters);
|
|
287
|
-
} else {
|
|
288
|
-
await this.sqs.sendMessage(messageParameters).promise();
|
|
289
|
-
}
|
|
290
|
-
} catch (error) {
|
|
291
|
-
if (failureMode === SQS_PUBLISH_FAILURE_MODES.CATCH) {
|
|
292
|
-
container.get(_Dependencies.DEFINITIONS.LOGGER).error(error);
|
|
293
|
-
return null;
|
|
294
|
-
}
|
|
295
|
-
throw error;
|
|
296
|
-
}
|
|
297
|
-
return queue;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Sends a message to a queue consumer running in serverless-offline.
|
|
302
|
-
*
|
|
303
|
-
* This method invokes the consumer function directly instead of sending the
|
|
304
|
-
* message to SQS, which requires a real or emulated SQS service not provided
|
|
305
|
-
* by serverless-offline. This works very well for local testing.
|
|
306
|
-
*
|
|
307
|
-
* @param queue
|
|
308
|
-
* @param messageParameters
|
|
309
|
-
*/
|
|
310
|
-
async publishOffline(queue, messageParameters) {
|
|
311
|
-
const container = this.getContainer();
|
|
312
|
-
if (!container.isOffline) {
|
|
313
|
-
throw new Error('Can only publishOffline while running serverless offline.');
|
|
314
|
-
}
|
|
315
|
-
const consumers = container.getConfiguration('QUEUE_CONSUMERS') || {};
|
|
316
|
-
const FunctionName = consumers[queue];
|
|
317
|
-
if (!FunctionName) {
|
|
318
|
-
throw new Error(`Queue consumer for queue ${queue} was not found. Please configure your application's QUEUE_CONSUMERS.`);
|
|
319
|
-
}
|
|
320
|
-
const InvocationType = 'RequestResponse';
|
|
321
|
-
const Payload = JSON.stringify({
|
|
322
|
-
Records: [{
|
|
323
|
-
body: messageParameters.MessageBody
|
|
324
|
-
}]
|
|
325
|
-
});
|
|
326
|
-
const parameters = {
|
|
327
|
-
FunctionName,
|
|
328
|
-
InvocationType,
|
|
329
|
-
Payload
|
|
330
|
-
};
|
|
331
|
-
await this.lambda.invoke(parameters).promise();
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Receive from message queue
|
|
336
|
-
*
|
|
337
|
-
* @param queue string
|
|
338
|
-
* @param timeout number
|
|
339
|
-
* @returns {Promise<any>}
|
|
340
|
-
*/
|
|
341
|
-
receive(queue, timeout = 15) {
|
|
342
|
-
const container = this.getContainer();
|
|
343
|
-
const queueUrl = this.queues[queue];
|
|
344
|
-
const Logger = container.get(_Dependencies.DEFINITIONS.LOGGER);
|
|
345
|
-
const Timer = container.get(_Dependencies.DEFINITIONS.TIMER);
|
|
346
|
-
const timerId = `sqs-receive-message-${(0, _uuid.v4)()} - Queue: '${queueUrl}'`;
|
|
347
|
-
return new Promise((resolve, reject) => {
|
|
348
|
-
Timer.start(timerId);
|
|
349
|
-
this.sqs.receiveMessage({
|
|
350
|
-
QueueUrl: queueUrl,
|
|
351
|
-
VisibilityTimeout: timeout,
|
|
352
|
-
MaxNumberOfMessages: 10
|
|
353
|
-
}, (error, data) => {
|
|
354
|
-
Timer.stop(timerId);
|
|
355
|
-
if (error) {
|
|
356
|
-
Logger.error(error);
|
|
357
|
-
return reject(error);
|
|
358
|
-
}
|
|
359
|
-
if (typeof data.Messages === 'undefined') {
|
|
360
|
-
return resolve([]);
|
|
361
|
-
}
|
|
362
|
-
return resolve(data.Messages.map(message => new _Message.default(message)));
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
exports.default = SQSService;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = void 0;
|
|
7
|
-
var _Dependencies = require("../Config/Dependencies");
|
|
8
|
-
var _DependencyAware = _interopRequireDefault(require("../DependencyInjection/DependencyAware.class"));
|
|
9
|
-
var _DependencyInjection = _interopRequireDefault(require("../DependencyInjection/DependencyInjection.class"));
|
|
10
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
-
/**
|
|
12
|
-
* TimerService class
|
|
13
|
-
*/
|
|
14
|
-
class TimerService extends _DependencyAware.default {
|
|
15
|
-
/**
|
|
16
|
-
* TimerService constructor
|
|
17
|
-
*
|
|
18
|
-
* @param di
|
|
19
|
-
*/
|
|
20
|
-
constructor(di) {
|
|
21
|
-
super(di);
|
|
22
|
-
this.timers = {};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Start timer
|
|
27
|
-
*
|
|
28
|
-
* @param identifier
|
|
29
|
-
*/
|
|
30
|
-
start(identifier) {
|
|
31
|
-
this.timers[identifier] = Date.now();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Stop timer
|
|
36
|
-
*
|
|
37
|
-
* @param identifier
|
|
38
|
-
*/
|
|
39
|
-
stop(identifier) {
|
|
40
|
-
if (typeof this.timers[identifier] !== 'undefined') {
|
|
41
|
-
const duration = Date.now() - this.timers[identifier];
|
|
42
|
-
this.getContainer().get(_Dependencies.DEFINITIONS.LOGGER).info(`Timing - ${identifier} took ${duration}ms to complete`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
exports.default = TimerService;
|