piper-utils 1.0.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.
- package/.babelrc +12 -0
- package/README.md +119 -0
- package/bin/main.js +1003 -0
- package/bin/main.js.LICENSE.txt +151 -0
- package/bin/main.js.map +1 -0
- package/bitbucket-pipelines.yml +14 -0
- package/jasmine.json +7 -0
- package/package.json +62 -0
- package/src/database/dbSetUp/migrations.js +45 -0
- package/src/database/dbUtils/queryStringUtils/accessRightsUtils.js +149 -0
- package/src/database/dbUtils/queryStringUtils/accessRightsUtils.test.js +177 -0
- package/src/database/dbUtils/queryStringUtils/createFilters.js +92 -0
- package/src/database/dbUtils/queryStringUtils/createFilters.test.js +37 -0
- package/src/database/dbUtils/queryStringUtils/createIncludes.js +45 -0
- package/src/database/dbUtils/queryStringUtils/createIncludes.test.js +20 -0
- package/src/database/dbUtils/queryStringUtils/createSort.js +36 -0
- package/src/database/dbUtils/queryStringUtils/createSort.test.js +60 -0
- package/src/database/dbUtils/queryStringUtils/defaultFilters.js +108 -0
- package/src/database/dbUtils/queryStringUtils/defaultFilters.test.js +50 -0
- package/src/database/dbUtils/queryStringUtils/findAll.js +42 -0
- package/src/database/dbUtils/queryStringUtils/findAll.test.js +79 -0
- package/src/database/dbUtils/queryStringUtils/mocks/mocks.js +127 -0
- package/src/dynamo/dynamoUtils.js +16 -0
- package/src/eventManager/handleEvents.js +32 -0
- package/src/eventManager/handleEvents.test.js +92 -0
- package/src/eventManager/handleFile.js +76 -0
- package/src/eventManager/handleFile.test.js +352 -0
- package/src/eventManager/publishEvents.js +99 -0
- package/src/eventManager/publishEvents.test.js +161 -0
- package/src/eventManager/watchBucket.js +41 -0
- package/src/eventManager/watchBucket.test.js +137 -0
- package/src/index.js +40 -0
- package/src/requestResonse/errorCodes.js +132 -0
- package/src/requestResonse/requestResponse.js +198 -0
- package/src/requestResonse/requestResponse.test.js +241 -0
- package/src/s3/S3Utils.js/S3Utils.js +20 -0
- package/src/sns/SNSUtils.js +18 -0
- package/webpack.config.js +50 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import DB from 'sequelize';
|
|
2
|
+
|
|
3
|
+
export const sequilizeSchema = {
|
|
4
|
+
id: { type: DB.INTEGER, allowNull: true, field: 'id' },
|
|
5
|
+
char: { type: DB.CHAR, allowNull: true, field: 'char' },
|
|
6
|
+
decimal: { type: DB.DECIMAL, allowNull: true, field: 'decimal' },
|
|
7
|
+
boolean: { type: DB.BOOLEAN, allowNull: true, field: 'boolean' },
|
|
8
|
+
date: { type: DB.DATE, allowNull: true, field: 'authCode' },
|
|
9
|
+
dateOnly: { type: DB.DATEONLY, allowNull: true, field: 'authCode' },
|
|
10
|
+
string: { type: DB.STRING(20), allowNull: true, field: 'string' },
|
|
11
|
+
instantiatedDecimal: { type: DB.DECIMAL(16, 2), allowNull: true, field: 'instantiatedDecimal' },
|
|
12
|
+
transactionStatus: { type: DB.INTEGER, filterType: DB.Op.in, allowNull: true }
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const defaultFilters = {
|
|
16
|
+
id: { filterType: DB.Op.eq },
|
|
17
|
+
char: { filterType: DB.Op.iLike },
|
|
18
|
+
decimal: { filterType: DB.Op.eq },
|
|
19
|
+
boolean: { filterType: DB.Op.eq }
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const defaultFiltersWithSubSchema = {
|
|
23
|
+
'snowman.snowmanType': { filterType: DB.Op.eq },
|
|
24
|
+
'gearMonkey.type': { filterType: DB.Op.eq },
|
|
25
|
+
'FLURB.status': { filterType: DB.Op.eq },
|
|
26
|
+
id: { filterType: DB.Op.eq },
|
|
27
|
+
char: { filterType: DB.Op.iLike },
|
|
28
|
+
decimal: { filterType: DB.Op.or },
|
|
29
|
+
boolean: { type: DB.BOOLEAN, filterType: DB.Op.eq },
|
|
30
|
+
array: { filterType: DB.Op.in }
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const event = {
|
|
34
|
+
'pathParameters': {
|
|
35
|
+
'id': '1'
|
|
36
|
+
},
|
|
37
|
+
'body': '{}',
|
|
38
|
+
'requestContext': {
|
|
39
|
+
'authorizer': {
|
|
40
|
+
'claims': {
|
|
41
|
+
'custom:businessIds': '[\'1\',\'2\',\'3\',\'4\']'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
'queryStringParameters': {
|
|
46
|
+
'snowman.snowmanType': 10,
|
|
47
|
+
'monkey.foo': 10,
|
|
48
|
+
'gearMonkey.type':10,
|
|
49
|
+
'limit': 22,
|
|
50
|
+
'offset': 14,
|
|
51
|
+
'endDate': '2017-01-01',
|
|
52
|
+
'startDate': '2001-01-01',
|
|
53
|
+
'businessIds': '1,2,3,4',
|
|
54
|
+
'id': '213235',
|
|
55
|
+
'char': 'StringToSearch',
|
|
56
|
+
'decimal': '4565',
|
|
57
|
+
'boolean': 'true',
|
|
58
|
+
'array': '10,20,30',
|
|
59
|
+
'date': '2001-9-9',
|
|
60
|
+
'notOnObject': 'no',
|
|
61
|
+
'sort': 'decimal,-id,boolean,notOnObject,-date,FLURB.id'
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const eventWithSingleElementInArray = {
|
|
66
|
+
'pathParameters': {
|
|
67
|
+
'id': '1'
|
|
68
|
+
},
|
|
69
|
+
'body': '{}',
|
|
70
|
+
'requestContext': {
|
|
71
|
+
'authorizer': {
|
|
72
|
+
'claims': {
|
|
73
|
+
'custom:businessIds': '[\'1\',\'2\',\'3\',\'4\']'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
'queryStringParameters': {
|
|
78
|
+
'snowman.snowmanType': 10,
|
|
79
|
+
'monkey.foo': 10,
|
|
80
|
+
'gearMonkey.type':10,
|
|
81
|
+
'limit': 22,
|
|
82
|
+
'offset': 14,
|
|
83
|
+
'endDate': '2017-01-01',
|
|
84
|
+
'startDate': '2001-01-01',
|
|
85
|
+
'businessIds': '1,2,3,4',
|
|
86
|
+
'id': '213235',
|
|
87
|
+
'char': 'StringToSearch',
|
|
88
|
+
'decimal': '4565',
|
|
89
|
+
'boolean': 'true',
|
|
90
|
+
'array': '10',
|
|
91
|
+
'date': '2001-9-9',
|
|
92
|
+
'notOnObject': 'no',
|
|
93
|
+
'sort': 'decimal,-id,boolean,notOnObject,-date,FLURB.id'
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const eventWithSingleElementAsArray = {
|
|
98
|
+
'pathParameters': {
|
|
99
|
+
'id': '1'
|
|
100
|
+
},
|
|
101
|
+
'body': '{}',
|
|
102
|
+
'requestContext': {
|
|
103
|
+
'authorizer': {
|
|
104
|
+
'claims': {
|
|
105
|
+
'custom:businessIds': '[\'1\',\'2\',\'3\',\'4\']'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
'queryStringParameters': {
|
|
110
|
+
'snowman.snowmanType': 10,
|
|
111
|
+
'monkey.foo': 10,
|
|
112
|
+
'gearMonkey.type':10,
|
|
113
|
+
'limit': 22,
|
|
114
|
+
'offset': 14,
|
|
115
|
+
'endDate': '2017-01-01',
|
|
116
|
+
'startDate': '2001-01-01',
|
|
117
|
+
'businessIds': '1,2,3,4',
|
|
118
|
+
'id': '213235',
|
|
119
|
+
'char': 'StringToSearch',
|
|
120
|
+
'decimal': '4565',
|
|
121
|
+
'boolean': 'true',
|
|
122
|
+
'array': '10',
|
|
123
|
+
'date': '2001-9-9',
|
|
124
|
+
'notOnObject': 'no',
|
|
125
|
+
'sort': 'decimal,-id,boolean,notOnObject,-date,FLURB.id'
|
|
126
|
+
}
|
|
127
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
3
|
+
|
|
4
|
+
const dynamoUtil = {
|
|
5
|
+
get: (params) => get(params)
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {{TableName:string, Key:{key:string}}} params
|
|
10
|
+
*/
|
|
11
|
+
async function get(params) {
|
|
12
|
+
const dynamoDb = DynamoDBDocument.from(new DynamoDB());
|
|
13
|
+
return dynamoDb.get(params);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default dynamoUtil;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { handleFile } from './handleFile.js';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import Promise from 'bluebird';
|
|
4
|
+
|
|
5
|
+
export function handleEvents(event, transformer, errorHandlerPerFile, shouldSkipFailedFolders = false, userImportTypes) {
|
|
6
|
+
if (!event || !event.Records) {
|
|
7
|
+
return Promise.resolve();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let hasErrors = false;
|
|
11
|
+
|
|
12
|
+
return Promise.map(event.Records, (record) => {
|
|
13
|
+
const snsInfo = JSON.parse(_.get(record, 'Sns.Message', '{}'));
|
|
14
|
+
const s3Bucket = snsInfo.bucket;
|
|
15
|
+
const files = snsInfo.files || [];
|
|
16
|
+
|
|
17
|
+
return Promise.map(files, (file) => {
|
|
18
|
+
let path = decodeURIComponent(file);
|
|
19
|
+
if (path) {
|
|
20
|
+
return handleFile(path, s3Bucket, transformer, { shouldSkipFailedFolders, userImportTypes }).catch((err) => {
|
|
21
|
+
if (errorHandlerPerFile && !errorHandlerPerFile(err, path)) {
|
|
22
|
+
hasErrors = true;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}).then(() => {
|
|
28
|
+
if (hasErrors) {
|
|
29
|
+
throw 'Errors processing one or more files. See error logs for detail on each error.';
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as handleEvents from './handleEvents.js';
|
|
2
|
+
import * as handleFile from './handleFile.js';
|
|
3
|
+
|
|
4
|
+
describe('handleEvents.js', () => {
|
|
5
|
+
describe('handleEvents', () => {
|
|
6
|
+
it('should run functions correctly for one file', function () {
|
|
7
|
+
const handleFileSpy = spyOn(handleFile, 'handleFile').and.returnValue(Promise.resolve());
|
|
8
|
+
|
|
9
|
+
return handleEvents.handleEvents({
|
|
10
|
+
Records: [{
|
|
11
|
+
'EventVersion': '1.0',
|
|
12
|
+
'EventSubscriptionArn': 'arn:aws:sns:EXAMPLE',
|
|
13
|
+
'EventSource': 'aws:sns',
|
|
14
|
+
'Sns': {
|
|
15
|
+
'SignatureVersion': '1',
|
|
16
|
+
'Timestamp': '1970-01-01T00:00:00.000Z',
|
|
17
|
+
'Signature': 'EXAMPLE',
|
|
18
|
+
'SigningCertUrl': 'EXAMPLE',
|
|
19
|
+
'MessageId': '95df01b4-ee98-5cb9-9903-4c221d41eb5e',
|
|
20
|
+
'Message': '{"bucket": "etl-input-transactions-development","files": ["failed-twice/A-emvio-plugin-transaction-6bbd68be-af30-49da-8e75-20d8d569ad22-2019-07-08T15_29_48.099Z.json"]}',
|
|
21
|
+
'MessageAttributes': {
|
|
22
|
+
'Test': {
|
|
23
|
+
'Type': 'String',
|
|
24
|
+
'Value': 'TestString'
|
|
25
|
+
},
|
|
26
|
+
'TestBinary': {
|
|
27
|
+
'Type': 'Binary',
|
|
28
|
+
'Value': 'TestBinary'
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
'Type': 'Notification',
|
|
32
|
+
'UnsubscribeUrl': 'EXAMPLE',
|
|
33
|
+
'TopicArn': 'arn:aws:sns:EXAMPLE',
|
|
34
|
+
'Subject': 'TestInvoke'
|
|
35
|
+
}
|
|
36
|
+
}]
|
|
37
|
+
}, {}, () => {
|
|
38
|
+
}).then(() => {
|
|
39
|
+
expect(handleFileSpy).toHaveBeenCalled();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should run errorHandlerPerFile if file errors', function () {
|
|
44
|
+
const handleFileSpy = spyOn(handleFile, 'handleFile').and.rejectWith();
|
|
45
|
+
const spy = jasmine.createSpy('errorSpy');
|
|
46
|
+
|
|
47
|
+
return handleEvents.handleEvents({
|
|
48
|
+
Records: [{
|
|
49
|
+
'EventVersion': '1.0',
|
|
50
|
+
'EventSubscriptionArn': 'arn:aws:sns:EXAMPLE',
|
|
51
|
+
'EventSource': 'aws:sns',
|
|
52
|
+
'Sns': {
|
|
53
|
+
'SignatureVersion': '1',
|
|
54
|
+
'Timestamp': '1970-01-01T00:00:00.000Z',
|
|
55
|
+
'Signature': 'EXAMPLE',
|
|
56
|
+
'SigningCertUrl': 'EXAMPLE',
|
|
57
|
+
'MessageId': '95df01b4-ee98-5cb9-9903-4c221d41eb5e',
|
|
58
|
+
'Message': '{"bucket": "etl-input-transactions-development","files": ["failed-twice/A-emvio-plugin-transaction-6bbd68be-af30-49da-8e75-20d8d569ad22-2019-07-08T15_29_48.099Z.json"]}',
|
|
59
|
+
'MessageAttributes': {
|
|
60
|
+
'Test': {
|
|
61
|
+
'Type': 'String',
|
|
62
|
+
'Value': 'TestString'
|
|
63
|
+
},
|
|
64
|
+
'TestBinary': {
|
|
65
|
+
'Type': 'Binary',
|
|
66
|
+
'Value': 'TestBinary'
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
'Type': 'Notification',
|
|
70
|
+
'UnsubscribeUrl': 'EXAMPLE',
|
|
71
|
+
'TopicArn': 'arn:aws:sns:EXAMPLE',
|
|
72
|
+
'Subject': 'TestInvoke'
|
|
73
|
+
}
|
|
74
|
+
}]
|
|
75
|
+
}, () => {}, spy).then(() => {
|
|
76
|
+
fail();
|
|
77
|
+
}).catch(() => {
|
|
78
|
+
expect(handleFileSpy).toHaveBeenCalled();
|
|
79
|
+
expect(spy).toHaveBeenCalled();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should return immediately if there is no Record on the event', () => {
|
|
84
|
+
try {
|
|
85
|
+
handleEvents.handleEvents({}, {}, () => {
|
|
86
|
+
});
|
|
87
|
+
} catch (error) {
|
|
88
|
+
expect(error).toBeUndefined();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import S3Util from '../s3/S3Utils.js/S3Utils.js';
|
|
3
|
+
|
|
4
|
+
export async function handleFile(path, s3Bucket, transformer, options) {
|
|
5
|
+
const shouldSkipFailedFolders = _.get(options, 'shouldSkipFailedFolders');
|
|
6
|
+
const userImportTypes = _.get(options, 'userImportTypes');
|
|
7
|
+
const params = {
|
|
8
|
+
Bucket: s3Bucket,
|
|
9
|
+
Key: path
|
|
10
|
+
};
|
|
11
|
+
let body = '{}'
|
|
12
|
+
try {
|
|
13
|
+
body = await S3Util.getObject(params);
|
|
14
|
+
const parsedBody = JSON.parse(body);
|
|
15
|
+
const returnedValue = await transformer(parsedBody);
|
|
16
|
+
await S3Util.deleteObject(params);
|
|
17
|
+
|
|
18
|
+
return returnedValue;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('handle-file-error', error);
|
|
21
|
+
|
|
22
|
+
if (error.code === 'NoSuchKey' || !body) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let newPath = '';
|
|
27
|
+
|
|
28
|
+
if (userImportTypes) {
|
|
29
|
+
// look for items in the userImportTypes
|
|
30
|
+
const usersImports = Object.values(userImportTypes);
|
|
31
|
+
const items = path.split('/');
|
|
32
|
+
const fileName = _.last(items);
|
|
33
|
+
const folderName = _.first(items);
|
|
34
|
+
|
|
35
|
+
if (usersImports.indexOf(folderName) > -1) {
|
|
36
|
+
if (shouldSkipFailedFolders) {
|
|
37
|
+
newPath = `${folderName}/error/${fileName}`;
|
|
38
|
+
} else if (_.startsWith(path, `${folderName}/failed-once/`)) {
|
|
39
|
+
newPath = _.replace(path, `${folderName}/failed-once/`, `${folderName}/failed-twice/`);
|
|
40
|
+
} else if (_.startsWith(path, `${folderName}/failed-twice/`)) {
|
|
41
|
+
newPath = _.replace(path, `${folderName}/failed-twice/`, `${folderName}/error/`);
|
|
42
|
+
} else {
|
|
43
|
+
newPath = `${folderName}/failed-once/${fileName}`;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
newPath = `pathAndEventMisMatchError/${path}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
} else {
|
|
50
|
+
if (shouldSkipFailedFolders) {
|
|
51
|
+
newPath = `error/${path}`;
|
|
52
|
+
} else if (_.startsWith(path, 'failed-once/')) {
|
|
53
|
+
newPath = _.replace(path, 'failed-once/', 'failed-twice/');
|
|
54
|
+
} else if (_.startsWith(path, 'failed-twice/')) {
|
|
55
|
+
newPath = _.replace(path, 'failed-twice/', 'error/');
|
|
56
|
+
} else {
|
|
57
|
+
if (_.includes(path, 'delayUntil/')) {
|
|
58
|
+
path = _.last(path.split('/'));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
newPath = `failed-once/${path}`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const newParams = {
|
|
66
|
+
Bucket: s3Bucket,
|
|
67
|
+
Key: newPath,
|
|
68
|
+
Body: body
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
await S3Util.putObject(newParams);
|
|
72
|
+
await S3Util.deleteObject(params);
|
|
73
|
+
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|