piper-utils 1.0.4 → 1.0.5-7.1
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 +1 -119
- package/bin/main.js +137 -107
- package/bin/main.js.map +1 -1
- package/package.json +3 -4
- package/src/database/dbUtils/queryStringUtils/accessRightsUtils.js +82 -36
- package/src/database/dbUtils/queryStringUtils/accessRightsUtils.test.js +368 -216
- package/src/database/dbUtils/queryStringUtils/createFilters.js +36 -1
- package/src/database/dbUtils/queryStringUtils/createFilters.test.js +21 -3
- package/src/database/dbUtils/queryStringUtils/findAll.js +2 -1
- package/src/database/dbUtils/queryStringUtils/mocks/mocks.js +43 -0
- package/src/eventManager/handleEvents.js +31 -1
- package/src/eventManager/handleFile.js +20 -3
- package/src/eventManager/handleFile.test.js +32 -3
- package/src/eventManager/watchBucket.js +22 -3
- package/src/eventManager/watchBucket.test.js +119 -0
- package/src/index.js +10 -0
- package/src/requestResonse/requestResponse.js +43 -6
- package/src/requestResonse/requestResponse.test.js +10 -1
- package/webpack.config.js +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createFilters } from './createFilters.js';
|
|
2
|
-
import { defaultFiltersWithSubSchema, event, eventWithSingleElementAsArray, eventWithSingleElementInArray } from './mocks/mocks.js';
|
|
3
|
-
import DB from 'sequelize';
|
|
2
|
+
import { defaultFiltersWithSubSchema, event, eventWithSearchString, eventWithSearchStringNumber, eventWithSingleElementAsArray, eventWithSingleElementInArray, manyStringFields } from './mocks/mocks.js';
|
|
3
|
+
import DB, { or } from 'sequelize';
|
|
4
4
|
|
|
5
5
|
describe('createFilters', function () {
|
|
6
|
-
it('should handle case insensitive searches', function (){
|
|
6
|
+
it('should handle case insensitive searches', function () {
|
|
7
7
|
const where = createFilters(event, defaultFiltersWithSubSchema);
|
|
8
8
|
expect(where.char).toEqual({ [DB.Op.iLike]: '%StringToSearch%' });
|
|
9
9
|
});
|
|
@@ -34,4 +34,22 @@ describe('createFilters', function () {
|
|
|
34
34
|
expect(where.notOnObject).not.toBeDefined();
|
|
35
35
|
expect(where.date).not.toBeDefined();
|
|
36
36
|
});
|
|
37
|
+
|
|
38
|
+
it('should handle searchString', function () {
|
|
39
|
+
const where = createFilters(eventWithSearchString, manyStringFields);
|
|
40
|
+
const orSymbols = Object.getOwnPropertySymbols(where);
|
|
41
|
+
expect(orSymbols.length).toBeGreaterThan(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle searchString if value is a number', function () {
|
|
45
|
+
const where = createFilters(eventWithSearchStringNumber, manyStringFields);
|
|
46
|
+
const orSymbols = Object.getOwnPropertySymbols(where);
|
|
47
|
+
expect(orSymbols.length).toBeGreaterThan(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle searchString sub fields', function () {
|
|
51
|
+
const where = createFilters(eventWithSearchStringNumber, defaultFiltersWithSubSchema);
|
|
52
|
+
const orSymbols = Object.getOwnPropertySymbols(where);
|
|
53
|
+
expect(orSymbols.length).toBeGreaterThan(0);
|
|
54
|
+
});
|
|
37
55
|
});
|
|
@@ -31,7 +31,8 @@ export function getPaginationFromFindAll(result, limit, offset) {
|
|
|
31
31
|
export async function findAll(model, options) {
|
|
32
32
|
|
|
33
33
|
const limit = options.limit || 0;
|
|
34
|
-
const
|
|
34
|
+
const includes = options.includes || { all: true, nested: true };
|
|
35
|
+
const findArgs = Object.assign({include: includes}, options, { limit: limit + 1 });
|
|
35
36
|
const results = await model.findAll(findArgs);
|
|
36
37
|
return getPaginationFromFindAll(results, limit, options.offset);
|
|
37
38
|
}
|
|
@@ -30,6 +30,16 @@ export const defaultFiltersWithSubSchema = {
|
|
|
30
30
|
array: { filterType: DB.Op.in }
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
export const manyStringFields = {
|
|
34
|
+
id: { filterType: DB.Op.eq },
|
|
35
|
+
name: { filterType: DB.Op.iLike },
|
|
36
|
+
title: { filterType: DB.Op.iLike },
|
|
37
|
+
size: { filterType: DB.Op.iLike },
|
|
38
|
+
decimal: { filterType: DB.Op.or },
|
|
39
|
+
boolean: { type: DB.BOOLEAN, filterType: DB.Op.eq },
|
|
40
|
+
array: { filterType: DB.Op.in }
|
|
41
|
+
};
|
|
42
|
+
|
|
33
43
|
export const event = {
|
|
34
44
|
'pathParameters': {
|
|
35
45
|
'id': '1'
|
|
@@ -61,6 +71,39 @@ export const event = {
|
|
|
61
71
|
'sort': 'decimal,-id,boolean,notOnObject,-date,FLURB.id'
|
|
62
72
|
}
|
|
63
73
|
};
|
|
74
|
+
export const eventWithSearchString = {
|
|
75
|
+
'pathParameters': {
|
|
76
|
+
'id': '1'
|
|
77
|
+
},
|
|
78
|
+
'body': '{}',
|
|
79
|
+
'requestContext': {
|
|
80
|
+
'authorizer': {
|
|
81
|
+
'claims': {
|
|
82
|
+
'custom:businessIds': '[\'1\',\'2\',\'3\',\'4\']'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
'queryStringParameters': {
|
|
87
|
+
'searchString': 'alpha-search'
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const eventWithSearchStringNumber = {
|
|
92
|
+
'pathParameters': {
|
|
93
|
+
'id': '1'
|
|
94
|
+
},
|
|
95
|
+
'body': '{}',
|
|
96
|
+
'requestContext': {
|
|
97
|
+
'authorizer': {
|
|
98
|
+
'claims': {
|
|
99
|
+
'custom:businessIds': '[\'1\',\'2\',\'3\',\'4\']'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
'queryStringParameters': {
|
|
104
|
+
'searchString': '900'
|
|
105
|
+
}
|
|
106
|
+
};
|
|
64
107
|
|
|
65
108
|
export const eventWithSingleElementInArray = {
|
|
66
109
|
'pathParameters': {
|
|
@@ -18,7 +18,9 @@ export function handleEvents(event, transformer, errorHandlerPerFile, shouldSkip
|
|
|
18
18
|
let path = decodeURIComponent(file);
|
|
19
19
|
if (path) {
|
|
20
20
|
return handleFile(path, s3Bucket, transformer, { shouldSkipFailedFolders, userImportTypes }).catch((err) => {
|
|
21
|
+
console.error('###############--@--->',err)
|
|
21
22
|
if (errorHandlerPerFile && !errorHandlerPerFile(err, path)) {
|
|
23
|
+
console.error('HANDLE-FILE-CATCH: ', err);
|
|
22
24
|
hasErrors = true;
|
|
23
25
|
}
|
|
24
26
|
});
|
|
@@ -26,7 +28,35 @@ export function handleEvents(event, transformer, errorHandlerPerFile, shouldSkip
|
|
|
26
28
|
});
|
|
27
29
|
}).then(() => {
|
|
28
30
|
if (hasErrors) {
|
|
29
|
-
throw '
|
|
31
|
+
throw 'ERROR: HANDLE-FILE';
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function handleDirectS3WriteEvent(event, transformer, errorHandlerPerFile, shouldSkipFailedFolders = false, userImportTypes) {
|
|
37
|
+
if (!event || !event.Records) {
|
|
38
|
+
return Promise.resolve();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let hasErrors = false;
|
|
42
|
+
|
|
43
|
+
return Promise.map(event.Records, (record) => {
|
|
44
|
+
const s3Bucket = record.s3.bucket.name;
|
|
45
|
+
const file = record.s3.object.key || [];
|
|
46
|
+
|
|
47
|
+
let path = decodeURIComponent(file);
|
|
48
|
+
if (path) {
|
|
49
|
+
return handleFile(path, s3Bucket, transformer, { shouldSkipFailedFolders, userImportTypes }).catch((err) => {
|
|
50
|
+
if (errorHandlerPerFile && !errorHandlerPerFile(err, path)) {
|
|
51
|
+
console.error('HANDLE-FILE-CATCH:', err);
|
|
52
|
+
hasErrors = true;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}).then(() => {
|
|
58
|
+
if (hasErrors) {
|
|
59
|
+
throw 'ERROR: HANDLE-FILE (DIRECT S3 EVENT)';
|
|
30
60
|
}
|
|
31
61
|
});
|
|
32
62
|
}
|
|
@@ -8,16 +8,19 @@ export async function handleFile(path, s3Bucket, transformer, options) {
|
|
|
8
8
|
Bucket: s3Bucket,
|
|
9
9
|
Key: path
|
|
10
10
|
};
|
|
11
|
-
let body = '{}'
|
|
11
|
+
let body = '{}';
|
|
12
12
|
try {
|
|
13
|
-
|
|
13
|
+
const s3r = await S3Util.getObject(params);
|
|
14
|
+
console.log('------->s3r', s3r);
|
|
15
|
+
body = s3r
|
|
14
16
|
const parsedBody = JSON.parse(body);
|
|
17
|
+
console.log('------->parsedBody-if this is empty body we need to skip transform?', parsedBody);
|
|
15
18
|
const returnedValue = await transformer(parsedBody);
|
|
16
19
|
await S3Util.deleteObject(params);
|
|
17
20
|
|
|
18
21
|
return returnedValue;
|
|
19
22
|
} catch (error) {
|
|
20
|
-
console.error('
|
|
23
|
+
console.error('HANDLE-FILE-ERROR', error);
|
|
21
24
|
|
|
22
25
|
if (error.code === 'NoSuchKey' || !body) {
|
|
23
26
|
return;
|
|
@@ -26,6 +29,7 @@ export async function handleFile(path, s3Bucket, transformer, options) {
|
|
|
26
29
|
let newPath = '';
|
|
27
30
|
|
|
28
31
|
if (userImportTypes) {
|
|
32
|
+
console.log('USER IMPORT')
|
|
29
33
|
// look for items in the userImportTypes
|
|
30
34
|
const usersImports = Object.values(userImportTypes);
|
|
31
35
|
const items = path.split('/');
|
|
@@ -68,9 +72,22 @@ export async function handleFile(path, s3Bucket, transformer, options) {
|
|
|
68
72
|
Body: body
|
|
69
73
|
};
|
|
70
74
|
|
|
75
|
+
if (containsErrorTwice(newPath, 'failed-once')
|
|
76
|
+
|| containsErrorTwice(newPath, 'failed-twice')
|
|
77
|
+
|| containsErrorTwice(newPath, 'error')) {
|
|
78
|
+
throw 'NESTING ERROR';
|
|
79
|
+
}
|
|
80
|
+
|
|
71
81
|
await S3Util.putObject(newParams);
|
|
72
82
|
await S3Util.deleteObject(params);
|
|
73
83
|
|
|
74
84
|
throw error;
|
|
75
85
|
}
|
|
76
86
|
}
|
|
87
|
+
|
|
88
|
+
export function containsErrorTwice(str, val) {
|
|
89
|
+
const firstIndex = str.toLowerCase().indexOf(val);
|
|
90
|
+
if (firstIndex === -1) return false;
|
|
91
|
+
const secondIndex = str.toLowerCase().indexOf(val, firstIndex + 1);
|
|
92
|
+
return secondIndex !== -1;
|
|
93
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Promise from 'bluebird';
|
|
2
|
-
import { handleFile } from './handleFile.js';
|
|
2
|
+
import { containsErrorTwice, handleFile } from './handleFile.js';
|
|
3
3
|
import S3Util from '../s3/S3Utils.js/S3Utils.js';
|
|
4
4
|
|
|
5
5
|
describe('handleFile', () => {
|
|
@@ -330,9 +330,9 @@ describe('handleFile', () => {
|
|
|
330
330
|
}
|
|
331
331
|
};
|
|
332
332
|
|
|
333
|
-
spyOn(S3Util, 'getObject').and.returnValue(Promise.resolve('{ "test": "val" }'
|
|
333
|
+
spyOn(S3Util, 'getObject').and.returnValue(Promise.resolve('{ "test": "val" }'));
|
|
334
334
|
spyOn(S3Util, 'deleteObject').and.returnValue(Promise.resolve('{}'));
|
|
335
|
-
const putObjectSpy = spyOn(S3Util, 'putObject').and.returnValue(Promise.resolve('{}'))
|
|
335
|
+
const putObjectSpy = spyOn(S3Util, 'putObject').and.returnValue(Promise.resolve('{}'));
|
|
336
336
|
|
|
337
337
|
spyOn(creator, 'createFunc').and.rejectWith({ message: 'THIS FUNCTION FAILED' });
|
|
338
338
|
const userImportTypes = {
|
|
@@ -349,4 +349,33 @@ describe('handleFile', () => {
|
|
|
349
349
|
});
|
|
350
350
|
});
|
|
351
351
|
});
|
|
352
|
+
it('should throw if nesting error folders', async () => {
|
|
353
|
+
const creator = {
|
|
354
|
+
createFunc: () => {
|
|
355
|
+
return Promise.resolve();
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
spyOn(S3Util, 'getObject').and.returnValue(Promise.resolve('{ "test": "val" }'));
|
|
360
|
+
spyOn(S3Util, 'deleteObject').and.returnValue(Promise.resolve('{}'));
|
|
361
|
+
const putObjectSpy = spyOn(S3Util, 'putObject').and.returnValue(Promise.resolve('{}'));
|
|
362
|
+
spyOn(creator, 'createFunc').and.rejectWith({ message: 'THIS FUNCTION FAILED' });
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
await handleFile('error/error/file.json', 'someBucket', creator.createFunc);
|
|
366
|
+
} catch (e) {
|
|
367
|
+
expect(e).toBe('NESTING ERROR');
|
|
368
|
+
expect(putObjectSpy).not.toHaveBeenCalled();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
});
|
|
372
|
+
it('containsErrorTwice: should throw if nesting error folders', async () => {
|
|
373
|
+
const r = containsErrorTwice('error/error', 'error');
|
|
374
|
+
expect(r).toBe(true);
|
|
375
|
+
|
|
376
|
+
});
|
|
377
|
+
it('containsErrorTwice: should throw if nesting error folders', async () => {
|
|
378
|
+
const r = containsErrorTwice('error/false', 'error');
|
|
379
|
+
expect(r).toBe(false);
|
|
380
|
+
});
|
|
352
381
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { publishEvents } from './publishEvents.js';
|
|
2
|
-
import { handleEvents } from './handleEvents.js';
|
|
2
|
+
import { handleEvents, handleDirectS3WriteEvent } from './handleEvents.js';
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* bucket watcher watches a s3 bucket and publishes events to a sns topic, this allows you to process files in s3 with a some transformer function
|
|
7
|
+
* prefix objects with DIRECT to listen to direct s3 events
|
|
7
8
|
*
|
|
8
9
|
* @param {object} params - The parameters for watchBucket, see below
|
|
9
10
|
* @param {object} params.event - The event of the lambda
|
|
@@ -14,7 +15,7 @@ import _ from 'lodash';
|
|
|
14
15
|
* @param {function} params.transformer - the function to run on each file
|
|
15
16
|
* @param {function} [params.errorHandlerPerFile] - the function to run on as a catch on each file
|
|
16
17
|
* @param {boolean} [params.shouldSkipFailedFolders] - whether to use the failed-once, failed-twice, error or just error folders
|
|
17
|
-
* @param {object} [params.userImportTypes] - user import
|
|
18
|
+
* @param {object} [params.userImportTypes] - user import subdirectories object map
|
|
18
19
|
*/
|
|
19
20
|
export function watchBucket({
|
|
20
21
|
event,
|
|
@@ -31,9 +32,27 @@ export function watchBucket({
|
|
|
31
32
|
throw new Error('Missing required parameters. Need event, dynamoConfigTable, dynamoConfigKey, s3Bucket, snsTopic, transformer');
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
const EventSource = _.get(event, 'Records[0].EventSource');
|
|
35
|
+
const EventSource = _.get(event, 'Records[0].eventSource') || _.get(event, 'Records[0].EventSource');
|
|
36
|
+
|
|
35
37
|
if (_.isEqual(EventSource, 'aws:sns')) {
|
|
36
38
|
return handleEvents(event, transformer, errorHandlerPerFile, shouldSkipFailedFolders, userImportTypes);
|
|
39
|
+
} else if (_.isEqual(EventSource, 'aws:s3')) {
|
|
40
|
+
const failedOnce = 'failed-once';
|
|
41
|
+
const failedTwice = 'failed-twice';
|
|
42
|
+
const error = 'error';
|
|
43
|
+
const direct = 'DIRECT';
|
|
44
|
+
const key = _.get(event, 'Records[0].s3.object.key') || '';
|
|
45
|
+
const startsWithDirect = _.startsWith(key, direct);
|
|
46
|
+
if (key.includes(failedOnce) || key.includes(failedTwice) || key.includes(error)) {
|
|
47
|
+
// this used to throw, but the throw raised useless alarms, I believed turing throw in to a return was ok
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (startsWithDirect) {
|
|
52
|
+
console.log('------->DIRECT WRITE FIRE');
|
|
53
|
+
return handleDirectS3WriteEvent(event, transformer, errorHandlerPerFile, shouldSkipFailedFolders, userImportTypes);
|
|
54
|
+
}
|
|
55
|
+
|
|
37
56
|
} else {
|
|
38
57
|
// the function is being run from the cron job so publish SNS events
|
|
39
58
|
return publishEvents(dynamoConfigTable, dynamoConfigKey, s3Bucket, snsTopic, userImportTypes);
|
|
@@ -3,6 +3,84 @@ import * as handleEvents from './handleEvents.js';
|
|
|
3
3
|
import * as publishEventsFunc from './publishEvents.js';
|
|
4
4
|
import _ from 'lodash';
|
|
5
5
|
|
|
6
|
+
const directEventMock = {
|
|
7
|
+
'Records': [
|
|
8
|
+
{
|
|
9
|
+
'eventVersion': '2.1',
|
|
10
|
+
'eventSource': 'aws:s3',
|
|
11
|
+
'awsRegion': 'us-east-1',
|
|
12
|
+
'eventTime': '2024-06-30T02:14:21.023Z',
|
|
13
|
+
'eventName': 'ObjectCreated:Put',
|
|
14
|
+
'userIdentity': {
|
|
15
|
+
'principalId': 'AWS:AROA5F2DYBZDQJZJL6QJ3:piper-development-updateOrder'
|
|
16
|
+
},
|
|
17
|
+
'requestParameters': {
|
|
18
|
+
'sourceIPAddress': '3.89.10.118'
|
|
19
|
+
},
|
|
20
|
+
'responseElements': {
|
|
21
|
+
'x-amz-request-id': 'TXTBHCFPQCG7G9XZ',
|
|
22
|
+
'x-amz-id-2': 'w/j8mQQpM6QcH0GOW2SQGh2yrj38NEeiOucQxnYODv77FuNYegVHwAm24B+EqcjuvhmRo1Kve28m8PzDHSENzokrGZC0r/xq'
|
|
23
|
+
},
|
|
24
|
+
's3': {
|
|
25
|
+
's3SchemaVersion': '1.0',
|
|
26
|
+
'configurationId': 'piper-hooks-development-handleWebhook-e8435433a831248ee12806555a8d414d',
|
|
27
|
+
'bucket': {
|
|
28
|
+
'name': 'notify-webhook-dev',
|
|
29
|
+
'ownerIdentity': {
|
|
30
|
+
'principalId': 'A27KTZMXKEPEYT'
|
|
31
|
+
},
|
|
32
|
+
'arn': 'arn:aws:s3:::notify-webhook-dev'
|
|
33
|
+
},
|
|
34
|
+
'object': {
|
|
35
|
+
'key': 'DIRECT-1z2HUO9nT2-106-orderUpdated-2024-06-30.json',
|
|
36
|
+
'size': 186,
|
|
37
|
+
'eTag': 'dca24fb9226703fcb0d64bd3e0833f16',
|
|
38
|
+
'sequencer': '006680BF7CF4E2AA36'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const errorBucketMock = {
|
|
46
|
+
'Records': [
|
|
47
|
+
{
|
|
48
|
+
'eventVersion': '2.1',
|
|
49
|
+
'eventSource': 'aws:s3',
|
|
50
|
+
'awsRegion': 'us-east-1',
|
|
51
|
+
'eventTime': '2024-06-30T03:33:47.071Z',
|
|
52
|
+
'eventName': 'ObjectCreated:Put',
|
|
53
|
+
'userIdentity': {
|
|
54
|
+
'principalId': 'AWS:AROA5F2DYBZD5OBZV274Z:piper-hooks-development-handleWebhook'
|
|
55
|
+
},
|
|
56
|
+
'requestParameters': {
|
|
57
|
+
'sourceIPAddress': '44.222.115.132'
|
|
58
|
+
},
|
|
59
|
+
'responseElements': {
|
|
60
|
+
'x-amz-request-id': 'P82WFW970DEXHPER',
|
|
61
|
+
'x-amz-id-2': 'ZfM/HalpIrLKJf+hx+q1ldaKoPH1IxUZtyXe1B80dx04lbiJk+9uge7pnEFRsBchGLdNEdeFwAO1+xUfOP9oCz19rZeHHSZU'
|
|
62
|
+
},
|
|
63
|
+
's3': {
|
|
64
|
+
's3SchemaVersion': '1.0',
|
|
65
|
+
'configurationId': 'piper-hooks-development-handleWebhook-e8435433a831248ee12806555a8d414d',
|
|
66
|
+
'bucket': {
|
|
67
|
+
'name': 'notify-webhook-dev',
|
|
68
|
+
'ownerIdentity': {
|
|
69
|
+
'principalId': 'A27KTZMXKEPEYT'
|
|
70
|
+
},
|
|
71
|
+
'arn': 'arn:aws:s3:::notify-webhook-dev'
|
|
72
|
+
},
|
|
73
|
+
'object': {
|
|
74
|
+
'key': 'error/1z2HUO9nT2-106-orderUpdated-2024-06-30.json',
|
|
75
|
+
'size': 2,
|
|
76
|
+
'eTag': '99914b932bd37a50b983c5e7c90ae93b',
|
|
77
|
+
'sequencer': '006680D21B0BAEE0A8'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
};
|
|
83
|
+
|
|
6
84
|
describe('watchBucket.js', () => {
|
|
7
85
|
describe('watchBucket', () => {
|
|
8
86
|
it('should fail missing event', function () {
|
|
@@ -133,5 +211,46 @@ describe('watchBucket.js', () => {
|
|
|
133
211
|
expect(publishEventsSpy).not.toHaveBeenCalled();
|
|
134
212
|
});
|
|
135
213
|
});
|
|
214
|
+
|
|
215
|
+
it('NOT run direct s3 calls if key does not start with DIRECT to prevent infinite loops', function () {
|
|
216
|
+
const params = {
|
|
217
|
+
event: directEventMock,
|
|
218
|
+
dynamoConfigTable: 'dynamoConfigTable',
|
|
219
|
+
dynamoConfigKey: 'dynamoConfigKey',
|
|
220
|
+
s3Bucket: 's3Bucket',
|
|
221
|
+
snsTopic: 'snsTopic',
|
|
222
|
+
transformer: _.noop
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const fromSnsSpy = spyOn(handleEvents, 'handleDirectS3WriteEvent').and.returnValue(Promise.resolve());
|
|
226
|
+
const publishEventsSpy = spyOn(publishEventsFunc, 'publishEvents').and.returnValue(Promise.resolve());
|
|
227
|
+
|
|
228
|
+
return watchBucket.watchBucket(params).then(() => {
|
|
229
|
+
expect(fromSnsSpy).toHaveBeenCalled();
|
|
230
|
+
expect(publishEventsSpy).not.toHaveBeenCalled();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('throw if a watch for s3 is fired when a file is rewritten to error folder', function () {
|
|
235
|
+
const params = {
|
|
236
|
+
event: errorBucketMock,
|
|
237
|
+
dynamoConfigTable: 'dynamoConfigTable',
|
|
238
|
+
dynamoConfigKey: 'dynamoConfigKey',
|
|
239
|
+
s3Bucket: 's3Bucket',
|
|
240
|
+
snsTopic: 'snsTopic',
|
|
241
|
+
transformer: _.noop
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const fromSnsSpy = spyOn(handleEvents, 'handleDirectS3WriteEvent').and.returnValue(Promise.resolve());
|
|
245
|
+
const publishEventsSpy = spyOn(publishEventsFunc, 'publishEvents').and.returnValue(Promise.resolve());
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
watchBucket.watchBucket(params);
|
|
249
|
+
expect(fromSnsSpy).not.toHaveBeenCalled();
|
|
250
|
+
expect(publishEventsSpy).not.toHaveBeenCalled();
|
|
251
|
+
} catch (e) {
|
|
252
|
+
expect(e).toBe('INVALID WATCH')
|
|
253
|
+
}
|
|
254
|
+
});
|
|
136
255
|
});
|
|
137
256
|
});
|
package/src/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { handleFile as handleFileImport } from './eventManager/handleFile.js';
|
|
2
2
|
import { watchBucket as watchBucketImport } from './eventManager/watchBucket.js';
|
|
3
3
|
import { publishEvents as publishEventsImport } from './eventManager/publishEvents.js';
|
|
4
|
+
import { createIncludes as createIncludesImport } from './database/dbUtils/queryStringUtils/createIncludes.js';
|
|
4
5
|
import { createFilters as createFiltersImport } from './database/dbUtils/queryStringUtils/createFilters.js';
|
|
5
6
|
import { createSort as createSortImport } from './database/dbUtils/queryStringUtils/createSort.js';
|
|
6
7
|
import { findAll as findAllImport } from './database/dbUtils/queryStringUtils/findAll.js';
|
|
7
8
|
import { checkModule as checkModuleImport } from './database/dbUtils/queryStringUtils/accessRightsUtils.js';
|
|
9
|
+
import { getModuleInfo as getModuleInfoImport } from './database/dbUtils/queryStringUtils/accessRightsUtils.js';
|
|
10
|
+
import { getAccessRightsInfo as getAccessRightsInfoImport } from './database/dbUtils/queryStringUtils/accessRightsUtils.js';
|
|
11
|
+
import { getDefaultBusinessIDInfo as getDefaultBusinessIDInfoImport } from './database/dbUtils/queryStringUtils/accessRightsUtils.js';
|
|
8
12
|
import { failure as failureImport, success as successImport } from './requestResonse/requestResponse.js';
|
|
9
13
|
import { runMigrations as runMigrationsImport } from './database/dbSetup/migrations.js';
|
|
10
14
|
import { getCurrentUser as getCurrentUserImport } from './requestResonse/requestResponse.js';
|
|
@@ -17,14 +21,19 @@ import { parseEvent as parseEventImport } from './requestResonse/requestResponse
|
|
|
17
21
|
import { getBusinessesInfo as getBusinessesInfoImport } from './database/dbUtils/queryStringUtils/accessRightsUtils.js';
|
|
18
22
|
import { userDefaultBid as userDefaultBidImport } from './database/dbUtils/queryStringUtils/accessRightsUtils.js';
|
|
19
23
|
import { checkWriteAccess as checkWriteAccessImport } from './database/dbUtils/queryStringUtils/accessRightsUtils.js';
|
|
24
|
+
import { isSystemUser as isSystemUserImport } from './database/dbUtils/queryStringUtils/accessRightsUtils.js';
|
|
20
25
|
|
|
21
26
|
export const handleFile = handleFileImport;
|
|
22
27
|
export const watchBucket = watchBucketImport;
|
|
23
28
|
export const publishEvents = publishEventsImport;
|
|
24
29
|
export const createFilters = createFiltersImport;
|
|
30
|
+
export const createIncludes = createIncludesImport;
|
|
25
31
|
export const createSort = createSortImport;
|
|
26
32
|
export const findAll = findAllImport;
|
|
27
33
|
export const checkModule = checkModuleImport;
|
|
34
|
+
export const getAccessRightsInfo = getAccessRightsInfoImport;
|
|
35
|
+
export const getDefaultBusinessIDInfo = getDefaultBusinessIDInfoImport;
|
|
36
|
+
export const getModuleInfo = getModuleInfoImport;
|
|
28
37
|
export const failure = failureImport;
|
|
29
38
|
export const success = successImport;
|
|
30
39
|
export const runMigrations = runMigrationsImport;
|
|
@@ -38,3 +47,4 @@ export const parseEvent = parseEventImport;
|
|
|
38
47
|
export const getBusinessesInfo = getBusinessesInfoImport;
|
|
39
48
|
export const userDefaultBid = userDefaultBidImport;
|
|
40
49
|
export const checkWriteAccess = checkWriteAccessImport;
|
|
50
|
+
export const isSystemUser = isSystemUserImport;
|
|
@@ -3,7 +3,7 @@ import { errorList } from './errorCodes.js';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @param {Object} body
|
|
6
|
-
* @param {{}} options
|
|
6
|
+
* @param {{dbClose?:function}} [options]
|
|
7
7
|
*/
|
|
8
8
|
export function success(body, options) {
|
|
9
9
|
const dbClose = _.get(options, 'dbClose', _.noop);
|
|
@@ -38,7 +38,7 @@ export function successHtml(html, options) {
|
|
|
38
38
|
export function getCurrentUserNameFromCognitoEvent(event) {
|
|
39
39
|
const attributes = _.get(event, 'request.userAttributes');
|
|
40
40
|
|
|
41
|
-
return attributes['cognito:email_alias'] || 'UNKNOWN USER';
|
|
41
|
+
return attributes['cognito:email_alias'] || attributes['email'] || 'UNKNOWN USER';
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -54,7 +54,7 @@ export function getCurrentUser(event) {
|
|
|
54
54
|
|
|
55
55
|
const id = JSON.parse(jsonToParse);
|
|
56
56
|
|
|
57
|
-
const username = _.get(event, 'requestContext.authorizer.claims.email') || _.get(event, 'requestContext.authorizer.email') || '
|
|
57
|
+
const username = _.get(event, 'requestContext.authorizer.claims.email') || _.get(event, 'requestContext.authorizer.email') || 'localtestuser@gexample.com';
|
|
58
58
|
|
|
59
59
|
return {
|
|
60
60
|
username,
|
|
@@ -74,7 +74,7 @@ export function failure(body = {}, options) {
|
|
|
74
74
|
dbClose();
|
|
75
75
|
let cleanedErrorBody;
|
|
76
76
|
|
|
77
|
-
if (process.env.UTIL_LOG === 'LOG_ALL') {
|
|
77
|
+
if (process.env.UTIL_LOG === 'LOG_ALL' || process.env.BUILD_ENV === 'test') {
|
|
78
78
|
console.error('------->UTIL ERROR:', body);
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -85,8 +85,8 @@ export function failure(body = {}, options) {
|
|
|
85
85
|
} else if (_.isUndefined(body.errorCode) || _.isUndefined(body.statusCode)) {
|
|
86
86
|
cleanedErrorBody = detectSequelizeError(body);
|
|
87
87
|
}
|
|
88
|
-
if(_.get(body, 'response.data.message')) {
|
|
89
|
-
console.error('------->UTIL ERROR: '+_.get(body, 'response.data.message'))
|
|
88
|
+
if (_.get(body, 'response.data.message')) {
|
|
89
|
+
console.error('------->UTIL ERROR: ' + _.get(body, 'response.data.message'));
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
const newBody = _.merge(NORMAL_ERROR, cleanedErrorBody);
|
|
@@ -194,5 +194,42 @@ export function detectJoyError(body) {
|
|
|
194
194
|
errorBody.message = joyError.message;
|
|
195
195
|
errorBody.statusCode = 400;
|
|
196
196
|
errorBody.errorCode = '4000';
|
|
197
|
+
return errorBody;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Parse the body of a Dynamoose error response.
|
|
202
|
+
* Attempts to extract a human-readable error message from Dynamoose errors.
|
|
203
|
+
* This is a fallback for unknown database errors.
|
|
204
|
+
*
|
|
205
|
+
* @param {object} body - Part of a body response containing error details.
|
|
206
|
+
* @returns {object} - Error message object { message: string }
|
|
207
|
+
*/
|
|
208
|
+
export function detectDynamooseError(body) {
|
|
209
|
+
const errorBody = {};
|
|
210
|
+
|
|
211
|
+
// Start with an empty message string.
|
|
212
|
+
let dynamooseError = '';
|
|
213
|
+
|
|
214
|
+
// If the error body contains an array of errors, concatenate their messages.
|
|
215
|
+
if (Array.isArray(body.errors)) {
|
|
216
|
+
dynamooseError += body.errors.reduce((acc, err) => {
|
|
217
|
+
return acc + ' ' + (err.message || '');
|
|
218
|
+
}, '');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// If there's a top-level message, add it.
|
|
222
|
+
if (body.message) {
|
|
223
|
+
dynamooseError += ' ' + body.message;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// If there is any additional detail provided, append it.
|
|
227
|
+
if (body.detail) {
|
|
228
|
+
dynamooseError += ' ' + body.detail;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Trim the message to remove extra spaces.
|
|
232
|
+
errorBody.message = dynamooseError.trim();
|
|
233
|
+
|
|
197
234
|
return errorBody;
|
|
198
235
|
}
|
|
@@ -95,7 +95,7 @@ describe('requestResponse', () => {
|
|
|
95
95
|
};
|
|
96
96
|
const res = getCurrentUser(event);
|
|
97
97
|
expect(res).toEqual({
|
|
98
|
-
username: '
|
|
98
|
+
username: 'localtestuser@gexample.com',
|
|
99
99
|
id: 0
|
|
100
100
|
});
|
|
101
101
|
});
|
|
@@ -139,6 +139,15 @@ describe('requestResponse', () => {
|
|
|
139
139
|
});
|
|
140
140
|
});
|
|
141
141
|
describe('parseBody', () => {
|
|
142
|
+
// fit('parse form data', () => {
|
|
143
|
+
// parse body does not parse form data
|
|
144
|
+
// const event = {
|
|
145
|
+
// body: 'threeDSMethodData=eyJ0aHJlZURTTWV0aG9kTm90aWZpY2F0aW9uVVJMIjoiaHR0cHM6Ly93ZWJob29rLnNpdGUvNGYyNjEyZGMtOTA0Ny00NDRmLWIwOTUtYzcwYjg0NDliYjZlIiwidGhyZWVEU1NlcnZlclRyYW5zSUQiOiIxOTI2NThkMS1jNjA0LTQwYjQtOWFiMC0zMzJiNjcyMWI0ZmMifQ%3D%3D'
|
|
146
|
+
// };
|
|
147
|
+
//
|
|
148
|
+
// const res = parseBody(event);
|
|
149
|
+
// expect(res).toEqual({ foo: 'bar' });
|
|
150
|
+
// });
|
|
142
151
|
it('parse a json string', () => {
|
|
143
152
|
const event = {
|
|
144
153
|
body: '{"foo":"bar"}'
|
package/webpack.config.js
CHANGED