@hestia-earth/pipeline-utils 0.13.0 → 0.13.2

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/src/progress.ts CHANGED
@@ -29,7 +29,11 @@ export class HestiaError extends Error {
29
29
  * @param key The file throwing this error
30
30
  * @param errors The errors
31
31
  */
32
- constructor(public message, public key: string, public errors: any = '') {
32
+ constructor(
33
+ public message,
34
+ public key: string,
35
+ public errors: any = ''
36
+ ) {
33
37
  super(message);
34
38
  }
35
39
 
@@ -46,30 +50,43 @@ const fileToProgress = (path: string) => `${path.split('.')[0]}.progress`;
46
50
 
47
51
  const uploadProgress = (step: string, path: string, Bucket: string, since?: Date, err?: HestiaError, details?: any) =>
48
52
  of(fileToProgress(path)).pipe(
49
- mergeMap(filepath => loadJSONFile<Partial<IFileProgress>>(filepath, Bucket).pipe(
50
- catchError(() => of({} as Partial<IFileProgress>)),
51
- mergeMap(data => uploadJSON(filepath, {
52
- step,
53
- success: !err,
54
- time: {
55
- ...(data.time || {}),
56
- ...(since ? { [step]: timeSpent(since) } : {})
57
- },
58
- details,
59
- error: err ? err.toString() : null
60
- }, 'application/json', Bucket))
61
- ))
53
+ mergeMap(filepath =>
54
+ loadJSONFile<Partial<IFileProgress>>(filepath, Bucket).pipe(
55
+ catchError(() => of({} as Partial<IFileProgress>)),
56
+ mergeMap(data =>
57
+ uploadJSON(
58
+ filepath,
59
+ {
60
+ step,
61
+ success: !err,
62
+ time: {
63
+ ...(data.time || {}),
64
+ ...(since ? { [step]: timeSpent(since) } : {})
65
+ },
66
+ details,
67
+ error: err ? err.toString() : null
68
+ },
69
+ 'application/json',
70
+ Bucket
71
+ )
72
+ )
73
+ )
74
+ )
62
75
  );
63
76
 
64
- export const handleError = (step: FileProgress, params: IFunctionParam, since?: Date, Bucket = bucket) =>
65
- (err: HestiaError) => uploadProgress(step, params.key, Bucket, since, err).pipe(
66
- mergeMap(() => publish(`${step}${publishFunctionSuffix.Error}`, params))
67
- );
77
+ export const handleError =
78
+ (step: FileProgress, params: IFunctionParam, since?: Date, Bucket = bucket) =>
79
+ (err: HestiaError) =>
80
+ uploadProgress(step, params.key, Bucket, since, err).pipe(
81
+ mergeMap(() => publish(`${step}${publishFunctionSuffix.Error}`, params))
82
+ );
68
83
 
69
- export const handleSuccess = (step: FileProgress, params: IFunctionParam, since?: Date, Bucket = bucket) =>
70
- (details?: any) => uploadProgress(step, params.key, Bucket, since, null, details).pipe(
71
- mergeMap(() => publish(`${step}${publishFunctionSuffix.Success}`, params))
72
- );
84
+ export const handleSuccess =
85
+ (step: FileProgress, params: IFunctionParam, since?: Date, Bucket = bucket) =>
86
+ (details?: any) =>
87
+ uploadProgress(step, params.key, Bucket, since, null, details).pipe(
88
+ mergeMap(() => publish(`${step}${publishFunctionSuffix.Success}`, params))
89
+ );
73
90
 
74
91
  export const defaultErrorHandler = (key: string) => (err: Error) =>
75
92
  throwError(new HestiaError(err.message, key, err.stack));
package/src/s3.ts CHANGED
@@ -32,43 +32,62 @@ export const getS3 = () => {
32
32
  };
33
33
 
34
34
  export const getObject = ({ Key, ...params }: AWS.S3.GetObjectRequest) =>
35
- from(getS3().getObject({ ...params, Key: decodeURI(Key) }).promise()).pipe(
35
+ from(
36
+ getS3()
37
+ .getObject({ ...params, Key: decodeURI(Key) })
38
+ .promise()
39
+ ).pipe(
36
40
  take(1),
37
41
  map(({ Body }) => Body.toString())
38
42
  );
39
43
 
40
44
  export const putObject = ({ Key, ...params }: AWS.S3.PutObjectRequest) =>
41
- from(getS3().putObject({ ...params, Key: decodeURI(Key) }).promise()).pipe(
45
+ from(
46
+ getS3()
47
+ .putObject({ ...params, Key: decodeURI(Key) })
48
+ .promise()
49
+ ).pipe(
42
50
  take(1),
43
51
  map(res => (res.$response.data || '').toString())
44
52
  );
45
53
 
46
54
  export const deleteObjects = (Objects: AWS.S3.Object[], Bucket = bucket) =>
47
- from(getS3().deleteObjects({
48
- Bucket,
49
- Delete: {
50
- Objects: Objects.map(({ Key }) => ({ Key }))
51
- }
52
- }).promise()).pipe(
55
+ from(
56
+ getS3()
57
+ .deleteObjects({
58
+ Bucket,
59
+ Delete: {
60
+ Objects: Objects.map(({ Key }) => ({ Key }))
61
+ }
62
+ })
63
+ .promise()
64
+ ).pipe(
53
65
  take(1),
54
66
  map(res => (res.$response.data || '').toString())
55
67
  );
56
68
 
57
69
  export const copyObject = (fromParams, toParams, MetadataDirective?: string) =>
58
- from(getS3().copyObject({
59
- Bucket: toParams.bucket,
60
- Key: decodeURI(toParams.key),
61
- CopySource: `${fromParams.bucket}/${fromParams.key}`,
62
- MetadataDirective
63
- }).promise()).pipe(
70
+ from(
71
+ getS3()
72
+ .copyObject({
73
+ Bucket: toParams.bucket,
74
+ Key: decodeURI(toParams.key),
75
+ CopySource: `${fromParams.bucket}/${fromParams.key}`,
76
+ MetadataDirective
77
+ })
78
+ .promise()
79
+ ).pipe(
64
80
  take(1),
65
81
  map(res => (res.$response.data || '').toString())
66
82
  );
67
83
 
68
- export const loadStream = (key: string, Bucket = bucket) => getS3().getObject({
69
- Bucket,
70
- Key: decodeURI(key)
71
- }).createReadStream();
84
+ export const loadStream = (key: string, Bucket = bucket) =>
85
+ getS3()
86
+ .getObject({
87
+ Bucket,
88
+ Key: decodeURI(key)
89
+ })
90
+ .createReadStream();
72
91
 
73
92
  export const loadFile = (key: string) => {
74
93
  const stream = loadStream(key);
@@ -86,80 +105,122 @@ export const loadFile = (key: string) => {
86
105
  return subject.asObservable();
87
106
  };
88
107
 
89
- export const loadTextFile = (key: string, Bucket = bucket) => isS3Mode ? getObject({
90
- Bucket,
91
- Key: key
92
- }).pipe(map(res => res.replace(/\"/g, ''))) : local.loadTextFile(Bucket)(key);
93
-
94
- export const loadJSONFile = <T>(key: string, Bucket = bucket): Observable<T> => isS3Mode ? getObject({
95
- Bucket,
96
- Key: key,
97
- ResponseContentType: 'application/json'
98
- }).pipe(map(res => JSON.parse(stripBOM(res)) as T)) : local.loadJSONFile(Bucket)(key);
99
-
100
- export const uploadFile = (filepath: string, key: string, Bucket = bucket) => putObject({
101
- Bucket,
102
- Key: key,
103
- Body: createReadStream(filepath, 'utf8')
104
- });
105
-
106
- export const uploadText = (key: string, data: string, Bucket = bucket) => isS3Mode ? putObject({
107
- Bucket,
108
- Key: key,
109
- Body: data
110
- }) : local.uploadText(Bucket)(key, data);
111
-
112
- export const uploadJSON = (key: string, data, type = 'application/json', Bucket = bucket) => isS3Mode ? putObject({
113
- Bucket,
114
- Key: key,
115
- Body: JSON.stringify(data, null, 2),
116
- ContentType: type
117
- }) : local.uploadJSON(Bucket)(key, data);
118
-
119
- export const waitObjectCreated = (key: string, Bucket = bucket) => isS3Mode ?
120
- from(getS3().waitFor('objectExists', {
108
+ export const loadTextFile = (key: string, Bucket = bucket) =>
109
+ isS3Mode
110
+ ? getObject({
111
+ Bucket,
112
+ Key: key
113
+ }).pipe(map(res => res.replace(/\"/g, '')))
114
+ : local.loadTextFile(Bucket)(key);
115
+
116
+ export const loadJSONFile = <T>(key: string, Bucket = bucket): Observable<T> =>
117
+ isS3Mode
118
+ ? getObject({
119
+ Bucket,
120
+ Key: key,
121
+ ResponseContentType: 'application/json'
122
+ }).pipe(map(res => JSON.parse(stripBOM(res)) as T))
123
+ : local.loadJSONFile(Bucket)(key);
124
+
125
+ export const uploadFile = (filepath: string, key: string, Bucket = bucket) =>
126
+ putObject({
121
127
  Bucket,
122
- Key: decodeURI(key)
123
- }).promise()).pipe(
124
- take(1),
125
- map(() => true)
126
- ) : of(true);
127
-
128
- const headObject = (key: string, Bucket = bucket) => from(getS3().headObject({
129
- Bucket,
130
- Key: decodeURI(key)
131
- }).promise());
132
-
133
- export const lastModified = (key: string, Bucket = bucket) => isS3Mode ? headObject(key, Bucket).pipe(
134
- map(({ LastModified }) => LastModified),
135
- catchError(() => of(null as Date)),
136
- take(1)
137
- ) : local.lastModified(Bucket)(key);
138
-
139
- export const fileExists = (key: string, Bucket = bucket) => isS3Mode ? headObject(key, Bucket).pipe(
140
- map(() => true),
141
- catchError(() => of(false)),
142
- take(1)
143
- ) : local.fileExists(Bucket)(key);
144
-
145
- export const fileSize = (key: string, Bucket = bucket) => isS3Mode ? headObject(key, Bucket).pipe(
146
- map(({ ContentLength }) => ContentLength),
147
- catchError(() => of(undefined as number)),
148
- take(1)
149
- ) : local.fileSize(Bucket)(key);
150
-
151
- export const deleteObject = (key: string, Bucket = bucket) => isS3Mode ? from(getS3().deleteObject({
152
- Bucket,
153
- Key: decodeURI(key)
154
- }).promise()) : local.deleteFile(Bucket)(key);
128
+ Key: key,
129
+ Body: createReadStream(filepath, 'utf8')
130
+ });
131
+
132
+ export const uploadText = (key: string, data: string, Bucket = bucket) =>
133
+ isS3Mode
134
+ ? putObject({
135
+ Bucket,
136
+ Key: key,
137
+ Body: data
138
+ })
139
+ : local.uploadText(Bucket)(key, data);
140
+
141
+ export const uploadJSON = (key: string, data, type = 'application/json', Bucket = bucket) =>
142
+ isS3Mode
143
+ ? putObject({
144
+ Bucket,
145
+ Key: key,
146
+ Body: JSON.stringify(data, null, 2),
147
+ ContentType: type
148
+ })
149
+ : local.uploadJSON(Bucket)(key, data);
150
+
151
+ export const waitObjectCreated = (key: string, Bucket = bucket) =>
152
+ isS3Mode
153
+ ? from(
154
+ getS3()
155
+ .waitFor('objectExists', {
156
+ Bucket,
157
+ Key: decodeURI(key)
158
+ })
159
+ .promise()
160
+ ).pipe(
161
+ take(1),
162
+ map(() => true)
163
+ )
164
+ : of(true);
165
+
166
+ const headObject = (key: string, Bucket = bucket) =>
167
+ from(
168
+ getS3()
169
+ .headObject({
170
+ Bucket,
171
+ Key: decodeURI(key)
172
+ })
173
+ .promise()
174
+ );
175
+
176
+ export const lastModified = (key: string, Bucket = bucket) =>
177
+ isS3Mode
178
+ ? headObject(key, Bucket).pipe(
179
+ map(({ LastModified }) => LastModified),
180
+ catchError(() => of(null as Date)),
181
+ take(1)
182
+ )
183
+ : local.lastModified(Bucket)(key);
184
+
185
+ export const fileExists = (key: string, Bucket = bucket) =>
186
+ isS3Mode
187
+ ? headObject(key, Bucket).pipe(
188
+ map(() => true),
189
+ catchError(() => of(false)),
190
+ take(1)
191
+ )
192
+ : local.fileExists(Bucket)(key);
193
+
194
+ export const fileSize = (key: string, Bucket = bucket) =>
195
+ isS3Mode
196
+ ? headObject(key, Bucket).pipe(
197
+ map(({ ContentLength }) => ContentLength),
198
+ catchError(() => of(undefined as number)),
199
+ take(1)
200
+ )
201
+ : local.fileSize(Bucket)(key);
202
+
203
+ export const deleteObject = (key: string, Bucket = bucket) =>
204
+ isS3Mode
205
+ ? from(
206
+ getS3()
207
+ .deleteObject({
208
+ Bucket,
209
+ Key: decodeURI(key)
210
+ })
211
+ .promise()
212
+ )
213
+ : local.deleteFile(Bucket)(key);
155
214
 
156
215
  export const listFolder = async (Prefix = '', Bucket = bucket, nextToken?: string): Promise<AWS.S3.Object[]> => {
157
- const { Contents, IsTruncated, NextContinuationToken } = await getS3().listObjectsV2({
158
- Bucket,
159
- Prefix,
160
- ContinuationToken: nextToken,
161
- MaxKeys: 1000
162
- }).promise();
216
+ const { Contents, IsTruncated, NextContinuationToken } = await getS3()
217
+ .listObjectsV2({
218
+ Bucket,
219
+ Prefix,
220
+ ContinuationToken: nextToken,
221
+ MaxKeys: 1000
222
+ })
223
+ .promise();
163
224
  return [...Contents, ...(IsTruncated ? await listFolder(Prefix, bucket, NextContinuationToken) : [])];
164
225
  };
165
226
 
package/src/sns.ts CHANGED
@@ -10,31 +10,39 @@ type attributes = {
10
10
  };
11
11
 
12
12
  export const publish = (
13
- functionName: string, params: IFunctionParam, topicArn = process.env.SNS_TOPIC, extras: attributes = {}
14
- ) => isS3Mode
15
- ? from(
16
- new AWS.SNS().publish({
17
- TopicArn: topicArn,
18
- Message: JSON.stringify(params),
19
- MessageAttributes: {
20
- functionName: {
21
- DataType: 'String',
22
- StringValue: functionName
23
- },
24
- ...(Object.fromEntries(Object.entries(extras || {}).map(([key, value]) => [
25
- key,
26
- {
27
- DataType: 'String',
28
- StringValue: value
29
- }
30
- ])))
31
- }
32
- }).promise()
33
- ).pipe(
34
- take(1),
35
- map(res => {
36
- debug(`SNS publish ${functionName}`, res);
37
- return res;
38
- })
39
- )
40
- : of(null);
13
+ functionName: string,
14
+ params: IFunctionParam | Record<string, any>,
15
+ topicArn = process.env.SNS_TOPIC,
16
+ extras: attributes = {}
17
+ ) =>
18
+ isS3Mode
19
+ ? from(
20
+ new AWS.SNS()
21
+ .publish({
22
+ TopicArn: topicArn,
23
+ Message: JSON.stringify(params),
24
+ MessageAttributes: {
25
+ functionName: {
26
+ DataType: 'String',
27
+ StringValue: functionName
28
+ },
29
+ ...Object.fromEntries(
30
+ Object.entries(extras || {}).map(([key, value]) => [
31
+ key,
32
+ {
33
+ DataType: 'String',
34
+ StringValue: value
35
+ }
36
+ ])
37
+ )
38
+ }
39
+ })
40
+ .promise()
41
+ ).pipe(
42
+ take(1),
43
+ map(res => {
44
+ debug(`SNS publish ${functionName}`, res);
45
+ return res;
46
+ })
47
+ )
48
+ : of(null);
package/src/sqs.ts CHANGED
@@ -10,31 +10,39 @@ type attributes = {
10
10
  };
11
11
 
12
12
  export const sendMessage = (
13
- functionName: string, params: IFunctionParam, queueUrl = process.env.SQS_URL, extras: attributes = {}
14
- ) => isS3Mode
15
- ? from(
16
- new AWS.SQS({ region }).sendMessage({
17
- QueueUrl: queueUrl,
18
- MessageBody: JSON.stringify(params),
19
- MessageAttributes: {
20
- functionName: {
21
- DataType: 'String',
22
- StringValue: functionName
23
- },
24
- ...(Object.fromEntries(Object.entries(extras || {}).map(([key, value]) => [
25
- key,
26
- {
27
- DataType: 'String',
28
- StringValue: value
29
- }
30
- ])))
31
- }
32
- }).promise()
33
- ).pipe(
34
- take(1),
35
- map(res => {
36
- debug(`SQS send message ${functionName}`, res);
37
- return res;
38
- })
39
- )
40
- : of(null);
13
+ functionName: string,
14
+ params: IFunctionParam | Record<string, any>,
15
+ queueUrl = process.env.SQS_URL,
16
+ extras: attributes = {}
17
+ ) =>
18
+ isS3Mode
19
+ ? from(
20
+ new AWS.SQS({ region })
21
+ .sendMessage({
22
+ QueueUrl: queueUrl,
23
+ MessageBody: JSON.stringify(params),
24
+ MessageAttributes: {
25
+ functionName: {
26
+ DataType: 'String',
27
+ StringValue: functionName
28
+ },
29
+ ...Object.fromEntries(
30
+ Object.entries(extras || {}).map(([key, value]) => [
31
+ key,
32
+ {
33
+ DataType: 'String',
34
+ StringValue: value
35
+ }
36
+ ])
37
+ )
38
+ }
39
+ })
40
+ .promise()
41
+ ).pipe(
42
+ take(1),
43
+ map(res => {
44
+ debug(`SQS send message ${functionName}`, res);
45
+ return res;
46
+ })
47
+ )
48
+ : of(null);
package/src/utils.ts CHANGED
@@ -6,31 +6,35 @@ import { error } from './log';
6
6
 
7
7
  export const recordToFunctionParam = (event: SNSEventRecord | S3EventRecord): IFunctionParam =>
8
8
  reduceUndefinedValues(
9
- 'Sns' in event ? {
10
- ...JSON.parse(event.Sns.Message),
11
- timestamp: event.Sns.Timestamp ? new Date(event.Sns.Timestamp) : undefined
12
- } : {
13
- bucket: event.s3.bucket.name,
14
- key: event.s3.object.key,
15
- filepath: event.s3.object.key,
16
- timestamp: event.eventTime ? new Date(event.eventTime) : undefined
17
- }
9
+ 'Sns' in event
10
+ ? {
11
+ ...JSON.parse(event.Sns.Message),
12
+ timestamp: event.Sns.Timestamp ? new Date(event.Sns.Timestamp) : undefined
13
+ }
14
+ : {
15
+ bucket: event.s3.bucket.name,
16
+ key: event.s3.object.key,
17
+ filepath: event.s3.object.key,
18
+ timestamp: event.eventTime ? new Date(event.eventTime) : undefined
19
+ }
18
20
  ) as IFunctionParam;
19
21
 
20
- export const functionParamToS3Record = ({ bucket, key, timestamp }: IFunctionParam) => ({
21
- s3: {
22
- bucket: { name: bucket },
23
- object: { key }
24
- },
25
- ...(timestamp ? { eventTime: timestamp.toJSON() } : {})
26
- }) as Partial<S3EventRecord>;
22
+ export const functionParamToS3Record = ({ bucket, key, timestamp }: IFunctionParam) =>
23
+ ({
24
+ s3: {
25
+ bucket: { name: bucket },
26
+ object: { key }
27
+ },
28
+ ...(timestamp ? { eventTime: timestamp.toJSON() } : {})
29
+ }) as Partial<S3EventRecord>;
27
30
 
28
- export const functionParamToSNSRecord = ({ bucket, key, timestamp }: IFunctionParam) => ({
29
- Sns: {
30
- Message: JSON.stringify({ bucket, key })
31
- },
32
- ...(timestamp ? { Timestamp: timestamp.toJSON() } : {})
33
- }) as Partial<SNSEventRecord>;
31
+ export const functionParamToSNSRecord = ({ bucket, key, timestamp }: IFunctionParam) =>
32
+ ({
33
+ Sns: {
34
+ Message: JSON.stringify({ bucket, key })
35
+ },
36
+ ...(timestamp ? { Timestamp: timestamp.toJSON() } : {})
37
+ }) as Partial<SNSEventRecord>;
34
38
 
35
39
  export const returnResponse = (data: any, statusCode = 200) => {
36
40
  return {
@@ -45,36 +49,39 @@ export const returnResponse = (data: any, statusCode = 200) => {
45
49
 
46
50
  const defaultErrorStatus = 500;
47
51
 
48
- export const returnError = (err) => {
52
+ export const returnError = err => {
49
53
  error('handleError', err, err?.stack, err?.meta);
50
- const { error: errorContent, status = defaultErrorStatus } = typeof err === 'string'
51
- ? { error: err }
52
- : err.meta
54
+ const { error: errorContent, status = defaultErrorStatus } =
55
+ typeof err === 'string'
56
+ ? { error: err }
57
+ : err.meta
53
58
  ? err.meta.body
54
59
  : { error: err.message, status: defaultErrorStatus };
55
60
  return returnResponse({ error: errorContent }, status || defaultErrorStatus);
56
61
  };
57
62
 
58
63
  export const spliceAndProcess = <T, R>(docs: T[], func: (p: T[]) => Promise<R>, count = 1) =>
59
- new Promise<R|void>(resolve => {
64
+ new Promise<R | void>(resolve => {
60
65
  if (!docs.length) {
61
66
  return resolve();
62
67
  }
63
68
  const remaining = docs.splice(docs.length - count);
64
- void func(remaining).then(() => spliceAndProcess(docs, func, count)).then(resolve);
69
+
70
+ void func(remaining)
71
+ .then(() => spliceAndProcess(docs, func, count))
72
+ .then(resolve);
65
73
  });
66
74
 
67
- export const isEmpty = (value: any, minKeys = 1) => typeof value === 'object' ?
68
- (
69
- Array.isArray(value) ?
70
- !value.length :
71
- Object.keys(value).filter(key => key !== 'type').length < minKeys
72
- ) :
73
- typeof value === 'undefined' || value === null || value === '';
75
+ export const isEmpty = (value: any, minKeys = 1) =>
76
+ typeof value === 'object'
77
+ ? Array.isArray(value)
78
+ ? !value.length
79
+ : Object.keys(value).filter(key => key !== 'type').length < minKeys
80
+ : typeof value === 'undefined' || value === null || value === '';
74
81
 
75
82
  export const refToSchemaType = ref => ref.substring(2).replace('.json#', '');
76
83
 
77
- export const prependWithKey = (data: any, key: string) => Object.keys(data)
78
- .reduce((prev, curr) => ({ ...prev, [`${key}.${curr}`]: data[curr] }), {});
84
+ export const prependWithKey = (data: any, key: string) =>
85
+ Object.keys(data).reduce((prev, curr) => ({ ...prev, [`${key}.${curr}`]: data[curr] }), {});
79
86
 
80
- export const stripBOM = (content: string) => content.charCodeAt(0) === 0xFEFF ? content.slice(1) : content;
87
+ export const stripBOM = (content: string) => (content.charCodeAt(0) === 0xfeff ? content.slice(1) : content);