@bitblit/ratchet-aws 4.0.115-alpha → 4.0.119-alpha

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.
Files changed (122) hide show
  1. package/lib/build/ratchet-aws-info.d.ts +1 -1
  2. package/lib/cloudwatch/cloud-watch-log-group-ratchet.d.ts +1 -0
  3. package/lib/cloudwatch/cloud-watch-logs-ratchet.d.ts +1 -0
  4. package/lib/cloudwatch/cloud-watch-metrics-ratchet.d.ts +2 -1
  5. package/lib/daemon/daemon-like.d.ts +1 -1
  6. package/lib/daemon/daemon-process-state-public-token.d.ts +1 -1
  7. package/lib/daemon/daemon-util.d.ts +2 -2
  8. package/lib/daemon/daemon.d.ts +2 -2
  9. package/lib/dynamodb/dynamo-ratchet.d.ts +1 -0
  10. package/lib/ec2/ec2-ratchet.d.ts +4 -2
  11. package/lib/index.d.ts +78 -1
  12. package/lib/index.mjs +11 -0
  13. package/lib/index.mjs.map +1 -0
  14. package/lib/model/cloud-watch-metrics-minute-level-dynamo-count-request.d.ts +1 -1
  15. package/lib/route53/route-53-ratchet.d.ts +1 -0
  16. package/lib/s3/s3-cache-ratchet.d.ts +3 -2
  17. package/lib/s3/s3-cache-to-local-disk-ratchet.d.ts +1 -1
  18. package/lib/ses/mailer.d.ts +1 -0
  19. package/lib/sns/sns-ratchet.d.ts +1 -0
  20. package/package.json +33 -103
  21. package/lib/batch/aws-batch-background-processor.js +0 -45
  22. package/lib/batch/aws-batch-background-processor.spec.js +0 -15
  23. package/lib/batch/aws-batch-ratchet.js +0 -55
  24. package/lib/batch/aws-batch-ratchet.spec.js +0 -33
  25. package/lib/build/ratchet-aws-info.js +0 -14
  26. package/lib/cache/dynamo-db-storage-provider.js +0 -109
  27. package/lib/cache/s3-storage-provider.js +0 -43
  28. package/lib/cache/simple-cache-object-wrapper.js +0 -1
  29. package/lib/cache/simple-cache-read-options.js +0 -1
  30. package/lib/cache/simple-cache-storage-provider.js +0 -1
  31. package/lib/cache/simple-cache.js +0 -64
  32. package/lib/cache/simple-cache.spec.js +0 -67
  33. package/lib/cloudwatch/cloud-watch-log-group-ratchet.js +0 -71
  34. package/lib/cloudwatch/cloud-watch-log-group-ratchet.spec.js +0 -19
  35. package/lib/cloudwatch/cloud-watch-logs-ratchet.js +0 -170
  36. package/lib/cloudwatch/cloud-watch-logs-ratchet.spec.js +0 -84
  37. package/lib/cloudwatch/cloud-watch-metrics-ratchet.js +0 -54
  38. package/lib/cloudwatch/cloud-watch-metrics-ratchet.spec.js +0 -23
  39. package/lib/daemon/daemon-like.js +0 -1
  40. package/lib/daemon/daemon-process-create-options.js +0 -1
  41. package/lib/daemon/daemon-process-state-public-token.js +0 -1
  42. package/lib/daemon/daemon-process-state.js +0 -1
  43. package/lib/daemon/daemon-util.js +0 -148
  44. package/lib/daemon/daemon-util.spec.js +0 -79
  45. package/lib/daemon/daemon.js +0 -128
  46. package/lib/dao/prototype-dao-config.js +0 -1
  47. package/lib/dao/prototype-dao-db.js +0 -1
  48. package/lib/dao/prototype-dao-provider.js +0 -1
  49. package/lib/dao/prototype-dao.js +0 -88
  50. package/lib/dao/prototype-dao.spec.js +0 -26
  51. package/lib/dao/s3-prototype-dao-provider.js +0 -26
  52. package/lib/dao/s3-simple-dao.js +0 -76
  53. package/lib/dao/simple-dao-item.js +0 -1
  54. package/lib/dynamodb/dynamo-ratchet-like.js +0 -1
  55. package/lib/dynamodb/dynamo-ratchet.js +0 -666
  56. package/lib/dynamodb/dynamo-ratchet.spec.js +0 -156
  57. package/lib/dynamodb/dynamo-table-ratchet.js +0 -88
  58. package/lib/dynamodb/hash-spreader.js +0 -65
  59. package/lib/dynamodb/hash-spreader.spec.js +0 -17
  60. package/lib/ec2/ec2-ratchet.js +0 -107
  61. package/lib/ec2/ec2-ratchet.spec.js +0 -31
  62. package/lib/environment/cascade-environment-service-provider.js +0 -24
  63. package/lib/environment/env-var-environment-service-provider.js +0 -30
  64. package/lib/environment/environment-service-config.js +0 -1
  65. package/lib/environment/environment-service-provider.js +0 -1
  66. package/lib/environment/environment-service.js +0 -50
  67. package/lib/environment/environment-service.spec.js +0 -22
  68. package/lib/environment/fixed-environment-service-provider.js +0 -21
  69. package/lib/environment/s3-environment-service-provider.js +0 -27
  70. package/lib/environment/ssm-environment-service-provider.js +0 -59
  71. package/lib/expiring-code/dynamo-expiring-code-provider.js +0 -24
  72. package/lib/expiring-code/expiring-code-params.js +0 -1
  73. package/lib/expiring-code/expiring-code-provider.js +0 -1
  74. package/lib/expiring-code/expiring-code-ratchet.js +0 -34
  75. package/lib/expiring-code/expiring-code-ratchet.spec.js +0 -7
  76. package/lib/expiring-code/expiring-code.js +0 -1
  77. package/lib/expiring-code/s3-expiring-code-provider.js +0 -48
  78. package/lib/expiring-code/s3-expiring-code-provider.spec.js +0 -46
  79. package/lib/iam/aws-credentials-ratchet.js +0 -18
  80. package/lib/index.js +0 -1
  81. package/lib/lambda/lambda-event-detector.js +0 -38
  82. package/lib/lambda/lambda-event-type-guards.js +0 -24
  83. package/lib/model/cloud-watch-metrics-minute-level-dynamo-count-request.js +0 -1
  84. package/lib/model/cloud-watch-metrics-unit.js +0 -30
  85. package/lib/model/dynamo/doc-put-item-command-input.js +0 -1
  86. package/lib/model/dynamo/doc-query-command-input.js +0 -1
  87. package/lib/model/dynamo/doc-scan-command-input.js +0 -1
  88. package/lib/model/dynamo/doc-update-item-command-input.js +0 -1
  89. package/lib/model/dynamo-count-result.js +0 -1
  90. package/lib/route53/route-53-ratchet.js +0 -55
  91. package/lib/runtime-parameter/cached-stored-runtime-parameter.js +0 -1
  92. package/lib/runtime-parameter/dynamo-runtime-parameter-provider.js +0 -36
  93. package/lib/runtime-parameter/dynamo-runtime-parameter-provider.spec.js +0 -49
  94. package/lib/runtime-parameter/global-variable-override-runtime-parameter-provider.js +0 -51
  95. package/lib/runtime-parameter/global-variable-override-runtime-parameter-provider.spec.js +0 -37
  96. package/lib/runtime-parameter/memory-runtime-parameter-provider.js +0 -27
  97. package/lib/runtime-parameter/runtime-parameter-provider.js +0 -1
  98. package/lib/runtime-parameter/runtime-parameter-ratchet.js +0 -71
  99. package/lib/runtime-parameter/runtime-parameter-ratchet.spec.js +0 -39
  100. package/lib/runtime-parameter/stored-runtime-parameter.js +0 -1
  101. package/lib/s3/s3-cache-ratchet.js +0 -330
  102. package/lib/s3/s3-cache-ratchet.spec.js +0 -97
  103. package/lib/s3/s3-cache-to-local-disk-ratchet.js +0 -105
  104. package/lib/s3/s3-cache-to-local-dist-ratchet.spec.js +0 -22
  105. package/lib/s3/s3-location-sync-ratchet.js +0 -140
  106. package/lib/s3/s3-ratchet.js +0 -22
  107. package/lib/s3/s3-ratchet.spec.js +0 -20
  108. package/lib/ses/email-attachment.js +0 -1
  109. package/lib/ses/mailer-config.js +0 -1
  110. package/lib/ses/mailer-like.js +0 -1
  111. package/lib/ses/mailer.js +0 -206
  112. package/lib/ses/mailer.spec.js +0 -104
  113. package/lib/ses/ratchet-template-renderer.js +0 -1
  114. package/lib/ses/ready-to-send-email.js +0 -1
  115. package/lib/ses/remote-handlebars-template-renderer.js +0 -78
  116. package/lib/ses/resolved-ready-to-send-email.js +0 -1
  117. package/lib/sns/sns-ratchet.js +0 -45
  118. package/lib/sns/sns-ratchet.spec.js +0 -17
  119. package/lib/sync-lock/dynamo-db-sync-lock.js +0 -69
  120. package/lib/sync-lock/dynamo-db-sync-lock.spec.js +0 -30
  121. package/lib/sync-lock/memory-sync-lock.js +0 -35
  122. package/lib/sync-lock/sync-lock-provider.js +0 -1
@@ -1,71 +0,0 @@
1
- import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
2
- import { RequireRatchet } from '@bitblit/ratchet-common/lib/lang/require-ratchet.js';
3
- export class RuntimeParameterRatchet {
4
- provider;
5
- cache = new Map();
6
- constructor(provider) {
7
- this.provider = provider;
8
- RequireRatchet.notNullOrUndefined(this.provider);
9
- }
10
- async fetchParameter(groupId, paramKey, defaultValue = null, forceFreshRead = false) {
11
- Logger.debug('Reading parameter %s / %s / Force : %s', groupId, paramKey, forceFreshRead);
12
- const cached = this.cache.get(RuntimeParameterRatchet.toCacheStoreKey(groupId, paramKey));
13
- let rval = null;
14
- const now = new Date().getTime();
15
- if (!forceFreshRead && !!cached) {
16
- const oldest = !!cached.ttlSeconds ? now - cached.ttlSeconds * 1000 : 0;
17
- if (cached.storedEpochMS > oldest) {
18
- Logger.silly('Fetched %s / %s from cache', groupId, paramKey);
19
- rval = JSON.parse(cached.paramValue);
20
- }
21
- }
22
- if (!rval) {
23
- const temp = await this.readUnderlyingEntry(groupId, paramKey);
24
- if (!!temp) {
25
- this.addToCache(temp);
26
- rval = JSON.parse(temp.paramValue);
27
- }
28
- }
29
- rval = rval || defaultValue;
30
- return rval;
31
- }
32
- async fetchAllParametersForGroup(groupId) {
33
- const all = await this.readUnderlyingEntries(groupId);
34
- const rval = new Map();
35
- all.forEach((t) => {
36
- rval.set(t.paramKey, JSON.parse(t.paramValue));
37
- this.addToCache(t);
38
- });
39
- return rval;
40
- }
41
- async readUnderlyingEntry(groupId, paramKey) {
42
- return this.provider.readParameter(groupId, paramKey);
43
- }
44
- async readUnderlyingEntries(groupId) {
45
- return this.provider.readAllParametersForGroup(groupId);
46
- }
47
- async storeParameter(groupId, paramKey, paramValue, ttlSeconds) {
48
- const toStore = {
49
- groupId: groupId,
50
- paramKey: paramKey,
51
- paramValue: JSON.stringify(paramValue),
52
- ttlSeconds: ttlSeconds,
53
- };
54
- const wrote = await this.provider.writeParameter(toStore);
55
- return this.provider.readParameter(groupId, paramKey);
56
- }
57
- static toCacheStoreKey(groupId, paramKey) {
58
- return groupId + ':::' + paramKey;
59
- }
60
- addToCache(temp) {
61
- if (!!temp) {
62
- const now = new Date().getTime();
63
- const toStore = Object.assign({ storedEpochMS: now }, temp);
64
- this.cache.set(RuntimeParameterRatchet.toCacheStoreKey(temp.groupId, temp.paramKey), toStore);
65
- }
66
- }
67
- clearCache() {
68
- Logger.debug('Clearing runtime parameter cache');
69
- this.cache = new Map();
70
- }
71
- }
@@ -1,39 +0,0 @@
1
- import { RuntimeParameterRatchet } from './runtime-parameter-ratchet.js';
2
- import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
3
- import { LoggerLevelName } from '@bitblit/ratchet-common/lib/logger/logger-level-name.js';
4
- import { PromiseRatchet } from '@bitblit/ratchet-common/lib/lang/promise-ratchet.js';
5
- import { MemoryRuntimeParameterProvider } from './memory-runtime-parameter-provider.js';
6
- const testEntry = { groupId: 'test', paramKey: 'test', paramValue: '15', ttlSeconds: 0.5 };
7
- const testEntry2 = { groupId: 'test', paramKey: 'test1', paramValue: '20', ttlSeconds: 0.5 };
8
- describe('#runtimeParameterRatchet', function () {
9
- it('fetch and cache a runtime parameter', async () => {
10
- Logger.setLevel(LoggerLevelName.silly);
11
- const mp = new MemoryRuntimeParameterProvider();
12
- const rpr = new RuntimeParameterRatchet(mp);
13
- const stored = await rpr.storeParameter('test', 'test1', 15, 0.5);
14
- Logger.info('Stored : %j', stored);
15
- const cache1 = await rpr.fetchParameter('test', 'test1');
16
- const cache1a = await rpr.fetchParameter('test', 'test1');
17
- const cache1b = await rpr.fetchParameter('test', 'test1');
18
- expect(cache1).toEqual(15);
19
- expect(cache1a).toEqual(15);
20
- expect(cache1b).toEqual(15);
21
- await PromiseRatchet.wait(1000);
22
- const cache2 = await rpr.fetchParameter('test', 'test1');
23
- expect(cache2).toEqual(15);
24
- const cacheMiss = await rpr.fetchParameter('test', 'test-miss');
25
- expect(cacheMiss).toBeNull();
26
- const cacheDefault = await rpr.fetchParameter('test', 'test-miss', 27);
27
- expect(cacheDefault).toEqual(27);
28
- }, 30_000);
29
- it('reads underlying entries', async () => {
30
- Logger.setLevel(LoggerLevelName.silly);
31
- const mrpp = new MemoryRuntimeParameterProvider();
32
- await mrpp.writeParameter(testEntry);
33
- await mrpp.writeParameter(testEntry2);
34
- const rpr = new RuntimeParameterRatchet(mrpp);
35
- const vals = await rpr.readUnderlyingEntries('test');
36
- expect(vals).not.toBeFalsy();
37
- expect(vals.length).toEqual(2);
38
- }, 30_000);
39
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,330 +0,0 @@
1
- import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsCommand, NoSuchKey, } from '@aws-sdk/client-s3';
2
- import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
3
- import { RequireRatchet } from '@bitblit/ratchet-common/lib/lang/require-ratchet.js';
4
- import { StringRatchet } from '@bitblit/ratchet-common/lib/lang/string-ratchet.js';
5
- import { StopWatch } from '@bitblit/ratchet-common/lib/lang/stop-watch.js';
6
- import { StreamRatchet } from '@bitblit/ratchet-common/lib/stream/stream-ratchet.js';
7
- import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
8
- import { Upload } from '@aws-sdk/lib-storage';
9
- export class S3CacheRatchet {
10
- s3;
11
- defaultBucket;
12
- constructor(s3, defaultBucket = null) {
13
- this.s3 = s3;
14
- this.defaultBucket = defaultBucket;
15
- RequireRatchet.notNullOrUndefined(this.s3, 's3');
16
- }
17
- static applyCacheControlMaxAge(input, seconds) {
18
- if (input && seconds) {
19
- input.CacheControl = 'max-age=' + seconds;
20
- }
21
- return input;
22
- }
23
- static applyUserMetaData(input, key, value) {
24
- if (input && StringRatchet.trimToNull(key) && StringRatchet.trimToNull(value)) {
25
- input.Metadata = input.Metadata || {};
26
- input.Metadata[key] = value;
27
- }
28
- return input;
29
- }
30
- getDefaultBucket() {
31
- return this.defaultBucket;
32
- }
33
- getS3Client() {
34
- return this.s3;
35
- }
36
- async fileExists(key, bucket = null) {
37
- try {
38
- const head = await this.fetchMetaForCacheFile(key, this.bucketVal(bucket));
39
- return !!head;
40
- }
41
- catch (err) {
42
- Logger.silly('Error calling file exists (as expected) %s', err);
43
- return false;
44
- }
45
- }
46
- async fetchCacheFileAsS3GetObjectCommandOutput(key, bucket = null) {
47
- let rval = null;
48
- try {
49
- const params = {
50
- Bucket: this.bucketVal(bucket),
51
- Key: key,
52
- };
53
- rval = await this.s3.send(new GetObjectCommand(params));
54
- }
55
- catch (err) {
56
- if (err instanceof NoSuchKey) {
57
- Logger.debug('Key %s not found - returning null', key);
58
- rval = null;
59
- }
60
- else {
61
- throw err;
62
- }
63
- }
64
- return rval;
65
- }
66
- async fetchCacheFileAsReadableStream(key, bucket = null) {
67
- const out = await this.fetchCacheFileAsS3GetObjectCommandOutput(key, bucket);
68
- return out.Body.transformToWebStream();
69
- }
70
- async fetchCacheFileAsBuffer(key, bucket = null) {
71
- let rval = null;
72
- const out = await this.fetchCacheFileAsS3GetObjectCommandOutput(key, bucket);
73
- if (out?.Body) {
74
- const tmp = await out.Body.transformToByteArray();
75
- rval = Buffer.from(tmp);
76
- }
77
- return rval;
78
- }
79
- async fetchCacheFileAsString(key, bucket = null) {
80
- let rval = null;
81
- const out = await this.fetchCacheFileAsS3GetObjectCommandOutput(key, bucket);
82
- if (out?.Body) {
83
- rval = await out.Body.transformToString();
84
- }
85
- return rval;
86
- }
87
- async fetchCacheFileAsObject(key, bucket = null) {
88
- const value = await this.fetchCacheFileAsString(key, bucket);
89
- return value ? JSON.parse(value) : null;
90
- }
91
- async removeCacheFile(key, bucket = null) {
92
- let rval = null;
93
- const params = {
94
- Bucket: this.bucketVal(bucket),
95
- Key: key,
96
- };
97
- try {
98
- rval = await this.s3.send(new DeleteObjectCommand(params));
99
- }
100
- catch (err) {
101
- if (err && err['statusCode'] == 404) {
102
- Logger.info('Swallowing 404 deleting missing object %s %s', bucket, key);
103
- rval = null;
104
- }
105
- else {
106
- throw err;
107
- }
108
- }
109
- return rval;
110
- }
111
- async writeObjectToCacheFile(key, dataObject, template, bucket) {
112
- const json = JSON.stringify(dataObject);
113
- return this.writeStringToCacheFile(key, json, template, bucket);
114
- }
115
- async writeStringToCacheFile(key, dataString, template, bucket) {
116
- const stream = StreamRatchet.stringToWebReadableStream(dataString);
117
- return this.writeStreamToCacheFile(key, stream, template, bucket);
118
- }
119
- async writeStreamToCacheFile(key, data, template, bucket) {
120
- const params = Object.assign({}, template || {}, {
121
- Bucket: this.bucketVal(bucket),
122
- Key: key,
123
- Body: data,
124
- });
125
- const upload = new Upload({
126
- client: this.s3,
127
- params: params,
128
- tags: [],
129
- queueSize: 4,
130
- partSize: 1024 * 1024 * 5,
131
- leavePartsOnError: false,
132
- });
133
- upload.on('httpUploadProgress', (progress) => {
134
- Logger.info('Uploading : %s', progress);
135
- });
136
- const result = await upload.done();
137
- return result;
138
- }
139
- async synchronize(srcPrefix, targetPrefix, targetRatchet = this, recurseSubFolders = false) {
140
- RequireRatchet.notNullOrUndefined(srcPrefix, 'srcPrefix');
141
- RequireRatchet.notNullOrUndefined(targetPrefix, 'targetPrefix');
142
- RequireRatchet.true(srcPrefix.endsWith('/'), 'srcPrefix must end in /');
143
- RequireRatchet.true(targetPrefix.endsWith('/'), 'targetPrefix must end in /');
144
- let rval = [];
145
- const sourceFiles = await this.directChildrenOfPrefix(srcPrefix);
146
- const targetFiles = await targetRatchet.directChildrenOfPrefix(targetPrefix);
147
- const sw = new StopWatch();
148
- for (let i = 0; i < sourceFiles.length; i++) {
149
- const sourceFile = sourceFiles[i];
150
- Logger.info('Processing %s : %s', sourceFile, sw.dumpExpected(i / sourceFiles.length));
151
- if (sourceFile.endsWith('/')) {
152
- if (recurseSubFolders) {
153
- Logger.info('%s is a subfolder - recursing');
154
- const subs = await this.synchronize(srcPrefix + sourceFile, targetPrefix + sourceFile, targetRatchet, recurseSubFolders);
155
- Logger.info('Got %d back from %s', subs.length, sourceFile);
156
- rval = rval.concat(subs);
157
- }
158
- else {
159
- Logger.info('%s is a subfolder and recurse not specified - skipping', sourceFile);
160
- }
161
- }
162
- else {
163
- let shouldCopy = true;
164
- const srcMeta = await this.fetchMetaForCacheFile(srcPrefix + sourceFile);
165
- if (targetFiles.includes(sourceFile)) {
166
- const targetMeta = await targetRatchet.fetchMetaForCacheFile(targetPrefix + sourceFile);
167
- if (srcMeta.ETag === targetMeta.ETag) {
168
- Logger.debug('Skipping - identical');
169
- shouldCopy = false;
170
- }
171
- }
172
- if (shouldCopy) {
173
- Logger.debug('Copying...');
174
- const srcStream = await this.fetchCacheFileAsReadableStream(srcPrefix + sourceFile);
175
- try {
176
- const written = await targetRatchet.writeStreamToCacheFile(targetPrefix + sourceFile, srcStream, srcMeta, undefined);
177
- Logger.silly('Write result : %j', written);
178
- rval.push(sourceFile);
179
- }
180
- catch (err) {
181
- Logger.error('Failed to sync : %s : %s', sourceFile, err);
182
- }
183
- }
184
- }
185
- }
186
- Logger.info('Found %d files, copied %d', sourceFiles.length, rval.length);
187
- sw.log();
188
- return rval;
189
- }
190
- async fetchMetaForCacheFile(key, bucket = null) {
191
- let rval = null;
192
- try {
193
- rval = await this.s3.send(new HeadObjectCommand({
194
- Bucket: this.bucketVal(bucket),
195
- Key: key,
196
- }));
197
- }
198
- catch (err) {
199
- if (err && err['statusCode'] == 404) {
200
- Logger.info('Cache file %s %s not found returning null', this.bucketVal(bucket), key);
201
- rval = null;
202
- }
203
- else {
204
- Logger.error('Unrecognized error, rethrowing : %s', err, err);
205
- throw err;
206
- }
207
- }
208
- return rval;
209
- }
210
- async cacheFileAgeInSeconds(key, bucket = null) {
211
- try {
212
- const res = await this.fetchMetaForCacheFile(key, bucket);
213
- if (res && res.LastModified) {
214
- return Math.floor((new Date().getTime() - res.LastModified.getTime()) / 1000);
215
- }
216
- else {
217
- Logger.warn('Cache file %s %s had no last modified returning null', this.bucketVal(bucket), key);
218
- return null;
219
- }
220
- }
221
- catch (err) {
222
- if (err && err['statusCode'] == 404) {
223
- Logger.warn('Cache file %s %s not found returning null', this.bucketVal(bucket), key);
224
- return null;
225
- }
226
- else {
227
- throw err;
228
- }
229
- }
230
- }
231
- async copyFile(srcKey, dstKey, srcBucket = null, dstBucket = null) {
232
- const params = {
233
- CopySource: '/' + this.bucketVal(srcBucket) + '/' + srcKey,
234
- Bucket: this.bucketVal(dstBucket),
235
- Key: dstKey,
236
- MetadataDirective: 'COPY',
237
- };
238
- const rval = await this.s3.send(new CopyObjectCommand(params));
239
- return rval;
240
- }
241
- async quietCopyFile(srcKey, dstKey, srcBucket = null, dstBucket = null) {
242
- let rval = false;
243
- try {
244
- const tmp = await this.copyFile(srcKey, dstKey, srcBucket, dstBucket);
245
- rval = true;
246
- }
247
- catch (err) {
248
- Logger.silly('Failed to copy file in S3 : %s', err);
249
- }
250
- return rval;
251
- }
252
- async preSignedDownloadUrlForCacheFile(key, expirationSeconds = 3600, bucket = null) {
253
- const getCommand = {
254
- Bucket: this.bucketVal(bucket),
255
- Key: key,
256
- };
257
- const link = await getSignedUrl(this.s3, new GetObjectCommand(getCommand), { expiresIn: expirationSeconds });
258
- return link;
259
- }
260
- async directChildrenOfPrefix(prefix, expandFiles = false, bucket = null, maxToReturn = null) {
261
- const returnValue = [];
262
- const params = {
263
- Bucket: this.bucketVal(bucket),
264
- Prefix: prefix,
265
- Delimiter: '/',
266
- };
267
- let response = null;
268
- do {
269
- response = await this.s3.send(new ListObjectsCommand(params));
270
- const prefixLength = prefix.length;
271
- if (response['CommonPrefixes']) {
272
- response['CommonPrefixes'].forEach((cp) => {
273
- if (!maxToReturn || returnValue.length < maxToReturn) {
274
- const value = cp['Prefix'].substring(prefixLength);
275
- returnValue.push(value);
276
- }
277
- });
278
- }
279
- if (response['Contents']) {
280
- await Promise.all(response['Contents'].map(async (cp) => {
281
- if (!maxToReturn || returnValue.length < maxToReturn) {
282
- if (expandFiles) {
283
- const expanded = {
284
- link: await this.preSignedDownloadUrlForCacheFile(cp['Key'], 3600, bucket),
285
- name: cp['Key'].substring(prefixLength),
286
- size: cp['Size'],
287
- };
288
- returnValue.push(expanded);
289
- }
290
- else {
291
- returnValue.push(cp['Key'].substring(prefixLength));
292
- }
293
- }
294
- }));
295
- }
296
- params.Marker = response.NextMarker;
297
- } while (params.Marker && (!maxToReturn || returnValue.length < maxToReturn));
298
- return returnValue;
299
- }
300
- async allSubFoldersOfPrefix(prefix, bucket = null) {
301
- const returnValue = [prefix];
302
- let idx = 0;
303
- while (idx < returnValue.length) {
304
- const next = returnValue[idx++];
305
- Logger.debug('Pulling %s (%d remaining)', next, returnValue.length - idx);
306
- const req = {
307
- Bucket: this.bucketVal(bucket),
308
- Prefix: next,
309
- Delimiter: '/',
310
- };
311
- let resp = null;
312
- do {
313
- req.ContinuationToken = resp ? resp.NextContinuationToken : null;
314
- resp = await this.s3.send(new ListObjectsCommand(req));
315
- resp.CommonPrefixes.forEach((p) => {
316
- returnValue.push(p.Prefix);
317
- });
318
- Logger.debug('g:%j', resp);
319
- } while (resp.NextContinuationToken);
320
- }
321
- return returnValue;
322
- }
323
- bucketVal(explicitBucket) {
324
- const rval = explicitBucket ? explicitBucket : this.defaultBucket;
325
- if (!rval) {
326
- throw 'You must set either the default bucket or pass it explicitly';
327
- }
328
- return rval;
329
- }
330
- }
@@ -1,97 +0,0 @@
1
- import { CopyObjectCommand, CreateMultipartUploadCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client, UploadPartCommand, } from '@aws-sdk/client-s3';
2
- import { S3CacheRatchet } from './s3-cache-ratchet.js';
3
- import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
4
- import { StringRatchet } from '@bitblit/ratchet-common/lib/lang/string-ratchet.js';
5
- import { StreamRatchet } from '@bitblit/ratchet-common/lib/stream/stream-ratchet.js';
6
- import { mockClient } from 'aws-sdk-client-mock';
7
- import { jest } from '@jest/globals';
8
- jest.mock('@aws-sdk/s3-request-presigner', () => ({
9
- getSignedUrl: jest.fn(() => Promise.resolve('https://test.link/test.jpg')),
10
- }));
11
- let mockS3 = null;
12
- let mockS3OtherAccount = null;
13
- describe('#fileExists', function () {
14
- mockS3 = mockClient(S3Client);
15
- mockS3OtherAccount = mockClient(S3Client);
16
- beforeEach(() => {
17
- mockS3.reset();
18
- mockS3OtherAccount.reset();
19
- });
20
- it('should return false for files that do not exist', async () => {
21
- mockS3.on(HeadObjectCommand).rejects({ statusCode: 404, $metadata: null });
22
- const cache = new S3CacheRatchet(mockS3, 'test-bucket');
23
- const out = await cache.fileExists('test-missing-file');
24
- expect(out).toEqual(false);
25
- });
26
- xit('should create a expiring link', async () => {
27
- const cache = new S3CacheRatchet(mockS3, 'test-bucket');
28
- const out = await cache.preSignedDownloadUrlForCacheFile('test.jpg', 300);
29
- expect(out).toEqual('https://test.link/test.jpg');
30
- });
31
- it('should copy an object', async () => {
32
- mockS3.on(CopyObjectCommand).resolves({});
33
- const cache = new S3CacheRatchet(mockS3, 'test-bucket');
34
- const out = await cache.quietCopyFile('test.png', 'test2.png');
35
- expect(out).toBeTruthy();
36
- });
37
- xit('should copy a file to s3', async () => {
38
- mockS3.on(PutObjectCommand).resolves({});
39
- mockS3.on(CreateMultipartUploadCommand).resolves({ UploadId: '1' });
40
- mockS3.on(UploadPartCommand).resolves({ ETag: '1' });
41
- const cache = new S3CacheRatchet(mockS3, 'test-bucket');
42
- const stream = StreamRatchet.stringToWebReadableStream(StringRatchet.createRandomHexString(1024 * 1024 * 6));
43
- const out = await cache.writeStreamToCacheFile('s3-cache-ratchet.spec.ts', stream, {
44
- ContentType: 'text/typescript',
45
- });
46
- Logger.info('Calls: %j', mockS3.calls());
47
- expect(out).toBeTruthy();
48
- });
49
- it('should pull a file as a string', async () => {
50
- async function createNew() {
51
- return {
52
- Body: {
53
- transformToByteArray: async () => Promise.resolve(StringRatchet.stringToUint8Array(JSON.stringify({ test: StringRatchet.createRandomHexString(128) }))),
54
- transformToString: async () => Promise.resolve(JSON.stringify({ test: StringRatchet.createRandomHexString(128) })),
55
- },
56
- $metadata: null,
57
- };
58
- }
59
- mockS3.on(GetObjectCommand).resolves(createNew());
60
- const cache = new S3CacheRatchet(mockS3, 'test-bucket');
61
- const fileName = 'test-file.json';
62
- const outBuf = await cache.fetchCacheFileAsBuffer(fileName);
63
- expect(outBuf).toBeTruthy();
64
- expect(outBuf.length).toBeGreaterThan(100);
65
- mockS3.reset();
66
- mockS3.on(GetObjectCommand).resolves(createNew());
67
- const outString = await cache.fetchCacheFileAsString(fileName);
68
- expect(outString).toBeTruthy();
69
- expect(outString.length).toBeGreaterThan(100);
70
- mockS3.reset();
71
- mockS3.on(GetObjectCommand).resolves(createNew());
72
- const outObject = await cache.fetchCacheFileAsObject(fileName);
73
- expect(outObject).toBeTruthy();
74
- expect(outObject['test']).toBeTruthy();
75
- });
76
- xit('should sync 2 folders', async () => {
77
- const cache1 = new S3CacheRatchet(mockS3, 'test1');
78
- const cache2 = new S3CacheRatchet(mockS3, 'test2');
79
- const out = await cache1.synchronize('src/', 'dst/', cache2);
80
- expect(out).not.toBeNull();
81
- }, 60_000);
82
- xit('should list direct children past 1000', async () => {
83
- const s3 = new S3Client({ region: 'us-east-1' });
84
- const cache = new S3CacheRatchet(s3, 'test-bucket');
85
- const out = await cache.directChildrenOfPrefix('test/aws/test-path-with-lots-of-childen/');
86
- expect(out).toBeTruthy();
87
- expect(out.length).toBeGreaterThan(1000);
88
- Logger.info('Got: %s', out);
89
- expect(out).toBeTruthy();
90
- });
91
- xit('should sync cross-account', async () => {
92
- const cache1 = new S3CacheRatchet(mockS3, 'bucket1');
93
- const cache2 = new S3CacheRatchet(mockS3OtherAccount, 'bucket2');
94
- const res = await cache1.synchronize('test1/', 'test2/', cache2, true);
95
- expect(res).not.toBeNull();
96
- }, 50_000);
97
- });
@@ -1,105 +0,0 @@
1
- import crypto from 'crypto';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
5
- import { RequireRatchet } from '@bitblit/ratchet-common/lib/lang/require-ratchet.js';
6
- import { StringRatchet } from '@bitblit/ratchet-common/lib/lang/string-ratchet.js';
7
- export class S3CacheToLocalDiskRatchet {
8
- s3;
9
- tmpFolder;
10
- cacheTimeoutSeconds;
11
- static DEFAULT_CACHE_TIMEOUT_SEC = 7 * 24 * 3600;
12
- currentlyLoading = new Map();
13
- constructor(s3, tmpFolder, cacheTimeoutSeconds = S3CacheToLocalDiskRatchet.DEFAULT_CACHE_TIMEOUT_SEC) {
14
- this.s3 = s3;
15
- this.tmpFolder = tmpFolder;
16
- this.cacheTimeoutSeconds = cacheTimeoutSeconds;
17
- RequireRatchet.notNullOrUndefined(s3, 's3');
18
- RequireRatchet.notNullOrUndefined(StringRatchet.trimToNull(tmpFolder));
19
- RequireRatchet.true(fs.existsSync(tmpFolder), 'folder must exist : ' + tmpFolder);
20
- }
21
- async getFileString(key) {
22
- const buf = await this.getFileBuffer(key);
23
- return buf ? buf.toString() : null;
24
- }
25
- keyToLocalCachePath(key) {
26
- const cachedHash = this.generateCacheHash(this.s3.getDefaultBucket() + '/' + key);
27
- const rval = path.join(this.tmpFolder, cachedHash);
28
- return rval;
29
- }
30
- removeCacheFileForKey(key) {
31
- const localCachePath = this.keyToLocalCachePath(key);
32
- Logger.info('Removing cache file for %s : %s', key, localCachePath);
33
- if (fs.existsSync(localCachePath)) {
34
- fs.unlinkSync(localCachePath);
35
- }
36
- else {
37
- Logger.debug('Skipping delete for %s - does not exist', localCachePath);
38
- }
39
- }
40
- async getFileBuffer(key) {
41
- const localCachePath = this.keyToLocalCachePath(key);
42
- let rval = null;
43
- rval = this.getCacheFileAsBuffer(localCachePath);
44
- if (!rval) {
45
- Logger.info('No cache. Downloading File s3://%s/%s to %s', this.s3.getDefaultBucket(), key, localCachePath);
46
- try {
47
- let prom = this.currentlyLoading.get(key);
48
- if (prom) {
49
- Logger.info('Already running - wait for that');
50
- }
51
- else {
52
- Logger.info('Not running - start');
53
- prom = this.updateLocalCacheFile(key, localCachePath);
54
- this.currentlyLoading.set(key, prom);
55
- }
56
- rval = await prom;
57
- this.currentlyLoading.delete(key);
58
- }
59
- catch (err) {
60
- Logger.warn('File %s/%s does not exist. Err code: %s', this.s3.getDefaultBucket(), key, err);
61
- }
62
- }
63
- else {
64
- Logger.info('Found cache file for s3://%s/%s. Local path %s', this.s3.getDefaultBucket(), key, localCachePath);
65
- }
66
- return rval;
67
- }
68
- async updateLocalCacheFile(key, localCachePath) {
69
- const rval = await this.s3.fetchCacheFileAsBuffer(key);
70
- if (rval && rval.length > 0) {
71
- Logger.info('Saving %d bytes to disk for cache', rval.length);
72
- fs.writeFileSync(localCachePath, rval);
73
- }
74
- return rval;
75
- }
76
- getCacheFileAsString(filePath) {
77
- const buf = this.getCacheFileAsBuffer(filePath);
78
- return buf ? buf.toString() : null;
79
- }
80
- getCacheFileAsBuffer(filePath) {
81
- if (!fs.existsSync(filePath)) {
82
- return null;
83
- }
84
- try {
85
- const stats = fs.statSync(filePath);
86
- const now = new Date().getTime();
87
- const duration = (now - stats.ctimeMs) / 1000;
88
- if (duration >= this.cacheTimeoutSeconds) {
89
- return null;
90
- }
91
- else {
92
- const rval = fs.readFileSync(filePath);
93
- return rval;
94
- }
95
- }
96
- catch (err) {
97
- Logger.warn('Error getting s3 cache file %s', err);
98
- }
99
- return null;
100
- }
101
- generateCacheHash(hashVal) {
102
- const rval = crypto.createHash('md5').update(hashVal).digest('hex');
103
- return rval;
104
- }
105
- }
@@ -1,22 +0,0 @@
1
- import { S3CacheToLocalDiskRatchet } from './s3-cache-to-local-disk-ratchet.js';
2
- import { tmpdir } from 'os';
3
- import { JestRatchet } from '@bitblit/ratchet-jest/lib/jest/jest-ratchet.js';
4
- import { jest } from '@jest/globals';
5
- let mockS3CR;
6
- describe('#S3CacheToLocalDiskRatchet', () => {
7
- beforeEach(() => {
8
- mockS3CR = JestRatchet.mock(jest.fn);
9
- });
10
- it('should download file and store in tmp', async () => {
11
- mockS3CR.fetchCacheFileAsBuffer.mockResolvedValue(Buffer.from(JSON.stringify({ a: 'b' })));
12
- const pth = 'test-path';
13
- const svc = new S3CacheToLocalDiskRatchet(mockS3CR, tmpdir());
14
- svc.removeCacheFileForKey(pth);
15
- const proms = [];
16
- for (let i = 0; i < 5; i++) {
17
- proms.push(svc.getFileBuffer(pth));
18
- }
19
- const all = await Promise.all(proms);
20
- expect(all.length).toEqual(5);
21
- });
22
- });