@cumulus/aws-client 11.1.4 → 11.1.6

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 CHANGED
@@ -855,7 +855,9 @@ Copy an S3 object to another location in S3 using a multipart copy
855
855
 
856
856
  **Kind**: inner method of [<code>S3</code>](#module_S3)
857
857
  **Returns**: <code>Promise.&lt;{etag: string}&gt;</code> - object containing the ETag of the
858
- destination object
858
+ destination object
859
+
860
+ note: this method may error if used with zero byte files. see CUMULUS-2557 and https://github.com/nasa/cumulus/pull/2117.
859
861
 
860
862
  | Param | Type | Default | Description |
861
863
  | --- | --- | --- | --- |
@@ -1163,12 +1165,12 @@ Class to use when interacting with S3
1163
1165
  **Kind**: global class
1164
1166
 
1165
1167
  * [S3ObjectStore](#S3ObjectStore)
1166
- * [.signGetObject(objectUrl, [options], [queryParams])](#S3ObjectStore+signGetObject) ⇒ <code>Promise.&lt;string&gt;</code>
1167
- * [.signHeadObject(objectUrl, [options], [queryParams])](#S3ObjectStore+signHeadObject) ⇒ <code>Promise.&lt;string&gt;</code>
1168
+ * [.signGetObject(objectUrl, [options], [queryParams], presignOptions)](#S3ObjectStore+signGetObject) ⇒ <code>Promise.&lt;string&gt;</code>
1169
+ * [.signHeadObject(objectUrl, [options], [queryParams], presignOptions)](#S3ObjectStore+signHeadObject) ⇒ <code>Promise.&lt;string&gt;</code>
1168
1170
 
1169
1171
  <a name="S3ObjectStore+signGetObject"></a>
1170
1172
 
1171
- ### s3ObjectStore.signGetObject(objectUrl, [options], [queryParams]) ⇒ <code>Promise.&lt;string&gt;</code>
1173
+ ### s3ObjectStore.signGetObject(objectUrl, [options], [queryParams], presignOptions) ⇒ <code>Promise.&lt;string&gt;</code>
1172
1174
  Returns an HTTPS URL that can be used to perform a GET on the given object
1173
1175
  store URL
1174
1176
 
@@ -1184,10 +1186,11 @@ store URL
1184
1186
  | objectUrl | <code>string</code> | the URL of the object to sign |
1185
1187
  | [options] | <code>string</code> | options to pass to S3.getObject |
1186
1188
  | [queryParams] | <code>string</code> | a mapping of parameter key/values to put in the URL |
1189
+ | presignOptions | <code>RequestPresigningArguments</code> | presignOptions |
1187
1190
 
1188
1191
  <a name="S3ObjectStore+signHeadObject"></a>
1189
1192
 
1190
- ### s3ObjectStore.signHeadObject(objectUrl, [options], [queryParams]) ⇒ <code>Promise.&lt;string&gt;</code>
1193
+ ### s3ObjectStore.signHeadObject(objectUrl, [options], [queryParams], presignOptions) ⇒ <code>Promise.&lt;string&gt;</code>
1191
1194
  Returns an HTTPS URL that can be used to perform a HEAD on the given object
1192
1195
  store URL
1193
1196
 
@@ -1203,6 +1206,7 @@ store URL
1203
1206
  | objectUrl | <code>string</code> | the URL of the object to sign |
1204
1207
  | [options] | <code>string</code> | options to pass to S3.getObject |
1205
1208
  | [queryParams] | <code>string</code> | a mapping of parameter key/values to put in the URL |
1209
+ | presignOptions | <code>RequestPresigningArguments</code> | presignOptions |
1206
1210
 
1207
1211
 
1208
1212
  ## About Cumulus
package/S3.d.ts CHANGED
@@ -409,6 +409,8 @@ export declare const createS3Buckets: (buckets: Array<string>) => Promise<any>;
409
409
  * @param {number} [params.chunkSize] - chunk size of the S3 multipart uploads
410
410
  * @returns {Promise.<{ etag: string }>} object containing the ETag of the
411
411
  * destination object
412
+ *
413
+ * note: this method may error if used with zero byte files. see CUMULUS-2557 and https://github.com/nasa/cumulus/pull/2117.
412
414
  */
413
415
  export declare const multipartCopyObject: (params: {
414
416
  sourceBucket: string;
package/S3.js CHANGED
@@ -800,6 +800,8 @@ const uploadPartCopy = async (params) => {
800
800
  * @param {number} [params.chunkSize] - chunk size of the S3 multipart uploads
801
801
  * @returns {Promise.<{ etag: string }>} object containing the ETag of the
802
802
  * destination object
803
+ *
804
+ * note: this method may error if used with zero byte files. see CUMULUS-2557 and https://github.com/nasa/cumulus/pull/2117.
803
805
  */
804
806
  const multipartCopyObject = async (params) => {
805
807
  var _a;
@@ -870,16 +872,38 @@ exports.multipartCopyObject = multipartCopyObject;
870
872
  * @returns {Promise<undefined>}
871
873
  */
872
874
  const moveObject = async (params) => {
873
- await (0, exports.multipartCopyObject)({
874
- sourceBucket: params.sourceBucket,
875
- sourceKey: params.sourceKey,
876
- destinationBucket: params.destinationBucket,
877
- destinationKey: params.destinationKey,
878
- ACL: params.ACL,
879
- copyTags: (0, isBoolean_1.default)(params.copyTags) ? params.copyTags : true,
880
- chunkSize: params.chunkSize,
881
- });
882
- return await (0, exports.deleteS3Object)(params.sourceBucket, params.sourceKey);
875
+ const { sourceBucket, sourceKey, destinationBucket, destinationKey, ACL, copyTags, chunkSize, } = params;
876
+ const sourceObject = await (0, exports.headObject)(sourceBucket, sourceKey);
877
+ if (sourceObject.ContentLength === 0) {
878
+ // 0 byte files cannot be copied with multipart upload,
879
+ // so use a regular S3 PUT
880
+ const s3uri = (0, exports.buildS3Uri)(destinationBucket, destinationKey);
881
+ const { CopyObjectResult } = await (0, exports.s3CopyObject)({
882
+ CopySource: path_1.default.join(sourceBucket, sourceKey),
883
+ Bucket: destinationBucket,
884
+ Key: destinationKey,
885
+ });
886
+ // This error should never actually be reached in practice. It's a
887
+ // necessary workaround for bad typings in the AWS SDK.
888
+ // https://github.com/aws/aws-sdk-js/issues/1719
889
+ if (!CopyObjectResult || !CopyObjectResult.ETag) {
890
+ throw new Error(`ETag could not be determined for copy of ${(0, exports.buildS3Uri)(sourceBucket, sourceKey)} to ${s3uri}`);
891
+ }
892
+ }
893
+ else {
894
+ await (0, exports.multipartCopyObject)({
895
+ sourceBucket: sourceBucket,
896
+ sourceKey: sourceKey,
897
+ destinationBucket: destinationBucket,
898
+ destinationKey: destinationKey,
899
+ sourceObject: sourceObject,
900
+ ACL: ACL,
901
+ copyTags: (0, isBoolean_1.default)(copyTags) ? copyTags : true,
902
+ chunkSize: chunkSize,
903
+ });
904
+ }
905
+ const deleteS3ObjRes = await (0, exports.deleteS3Object)(sourceBucket, sourceKey);
906
+ return deleteS3ObjRes;
883
907
  };
884
908
  exports.moveObject = moveObject;
885
909
  //# sourceMappingURL=S3.js.map
@@ -1,6 +1,7 @@
1
1
  import { GetObjectCommand, HeadObjectCommand, GetObjectCommandInput, S3ClientConfig } from '@aws-sdk/client-s3';
2
+ import { RequestPresigningArguments } from '@aws-sdk/types';
2
3
  declare type QueryParams = {
3
- [key: string]: number | string;
4
+ [key: string]: string;
4
5
  };
5
6
  /**
6
7
  * Class to use when interacting with S3
@@ -13,7 +14,7 @@ declare class S3ObjectStore {
13
14
  constructor(config?: Partial<S3ClientConfig>);
14
15
  getQueryParams(): QueryParams;
15
16
  setQueryParams(queryParams: QueryParams): void;
16
- getS3SignedUrlWithCustomQueryParams(command: GetObjectCommand | HeadObjectCommand): Promise<string>;
17
+ getS3SignedUrlWithCustomQueryParams(command: GetObjectCommand | HeadObjectCommand, presignOptions?: RequestPresigningArguments): Promise<string>;
17
18
  /**
18
19
  * Returns an HTTPS URL that can be used to perform a GET on the given object
19
20
  * store URL
@@ -21,10 +22,11 @@ declare class S3ObjectStore {
21
22
  * @param {string} objectUrl - the URL of the object to sign
22
23
  * @param {string} [options] - options to pass to S3.getObject
23
24
  * @param {string} [queryParams] - a mapping of parameter key/values to put in the URL
25
+ * @param {RequestPresigningArguments} presignOptions - presignOptions
24
26
  * @returns {Promise<string>} a signed URL
25
27
  * @throws TypeError - if the URL is not a recognized protocol or cannot be parsed
26
28
  */
27
- signGetObject(objectUrl: string, options?: Partial<GetObjectCommandInput>, queryParams?: QueryParams): Promise<string>;
29
+ signGetObject(objectUrl: string, options?: Partial<GetObjectCommandInput>, queryParams?: QueryParams, presignOptions?: RequestPresigningArguments): Promise<string>;
28
30
  /**
29
31
  * Returns an HTTPS URL that can be used to perform a HEAD on the given object
30
32
  * store URL
@@ -32,12 +34,13 @@ declare class S3ObjectStore {
32
34
  * @param {string} objectUrl - the URL of the object to sign
33
35
  * @param {string} [options] - options to pass to S3.getObject
34
36
  * @param {string} [queryParams] - a mapping of parameter key/values to put in the URL
37
+ * @param {RequestPresigningArguments} presignOptions - presignOptions
35
38
  * @returns {Promise<string>} a signed URL
36
39
  * @throws TypeError - if the URL is not a recognized protocol or cannot be parsed
37
40
  */
38
41
  signHeadObject(objectUrl: string, options: {
39
42
  [key: string]: string;
40
- } | undefined, queryParams: QueryParams): Promise<string>;
43
+ } | undefined, queryParams: QueryParams, presignOptions?: RequestPresigningArguments): Promise<string>;
41
44
  }
42
45
  export = S3ObjectStore;
43
46
  //# sourceMappingURL=S3ObjectStore.d.ts.map
package/S3ObjectStore.js CHANGED
@@ -11,6 +11,7 @@ const S3_1 = require("./S3");
11
11
  const client_1 = __importDefault(require("./client"));
12
12
  // Code modified from https://github.com/nasa/harmony/blob/main/app/util/object-store.ts
13
13
  const log = new logger_1.default({ sender: '@cumulus/aws-client/S3ObjectStore' });
14
+ const S3_LINK_EXPIRY_SECONDS_DEFAULT = 3600;
14
15
  /**
15
16
  * Class to use when interacting with S3
16
17
  *
@@ -29,7 +30,7 @@ class S3ObjectStore {
29
30
  setQueryParams(queryParams) {
30
31
  this.queryParams = queryParams;
31
32
  }
32
- async getS3SignedUrlWithCustomQueryParams(command) {
33
+ async getS3SignedUrlWithCustomQueryParams(command, presignOptions = {}) {
33
34
  this.s3.middlewareStack.addRelativeTo((next) => (args) => {
34
35
  const { request } = args;
35
36
  request.query = {
@@ -42,7 +43,7 @@ class S3ObjectStore {
42
43
  relation: 'before',
43
44
  toMiddleware: 'presignInterceptMiddleware',
44
45
  });
45
- const signedUrl = await (0, s3_request_presigner_1.getSignedUrl)(this.s3, command);
46
+ const signedUrl = await (0, s3_request_presigner_1.getSignedUrl)(this.s3, command, { expiresIn: S3_LINK_EXPIRY_SECONDS_DEFAULT, ...presignOptions });
46
47
  this.s3.middlewareStack.remove(this.middlewareName);
47
48
  return signedUrl;
48
49
  }
@@ -53,10 +54,11 @@ class S3ObjectStore {
53
54
  * @param {string} objectUrl - the URL of the object to sign
54
55
  * @param {string} [options] - options to pass to S3.getObject
55
56
  * @param {string} [queryParams] - a mapping of parameter key/values to put in the URL
57
+ * @param {RequestPresigningArguments} presignOptions - presignOptions
56
58
  * @returns {Promise<string>} a signed URL
57
59
  * @throws TypeError - if the URL is not a recognized protocol or cannot be parsed
58
60
  */
59
- async signGetObject(objectUrl, options = {}, queryParams = {}) {
61
+ async signGetObject(objectUrl, options = {}, queryParams = {}, presignOptions = {}) {
60
62
  log.info(`Executing signGetObject with objectUrl: ${objectUrl}, options: ${JSON.stringify(options)}, queryParams: ${JSON.stringify(queryParams)}`);
61
63
  const url = new url_1.URL(objectUrl);
62
64
  if (url.protocol.toLowerCase() !== 's3:') {
@@ -66,7 +68,7 @@ class S3ObjectStore {
66
68
  await (0, S3_1.headObject)(Bucket, Key);
67
69
  const command = new client_s3_1.GetObjectCommand({ Bucket, Key, ...options });
68
70
  this.setQueryParams(queryParams);
69
- const signedUrl = await this.getS3SignedUrlWithCustomQueryParams(command);
71
+ const signedUrl = await this.getS3SignedUrlWithCustomQueryParams(command, presignOptions);
70
72
  log.debug(`Signed GetObject request URL: ${signedUrl}`);
71
73
  return signedUrl;
72
74
  }
@@ -77,10 +79,11 @@ class S3ObjectStore {
77
79
  * @param {string} objectUrl - the URL of the object to sign
78
80
  * @param {string} [options] - options to pass to S3.getObject
79
81
  * @param {string} [queryParams] - a mapping of parameter key/values to put in the URL
82
+ * @param {RequestPresigningArguments} presignOptions - presignOptions
80
83
  * @returns {Promise<string>} a signed URL
81
84
  * @throws TypeError - if the URL is not a recognized protocol or cannot be parsed
82
85
  */
83
- async signHeadObject(objectUrl, options = {}, queryParams) {
86
+ async signHeadObject(objectUrl, options = {}, queryParams, presignOptions = {}) {
84
87
  log.info(`Executing signHeadObject with objectUrl: ${objectUrl}, options: ${JSON.stringify(options)}, queryParams: ${JSON.stringify(queryParams)}`);
85
88
  const url = new url_1.URL(objectUrl);
86
89
  if (url.protocol.toLowerCase() !== 's3:') {
@@ -89,7 +92,7 @@ class S3ObjectStore {
89
92
  const { Bucket, Key } = (0, S3_1.parseS3Uri)(objectUrl);
90
93
  const command = new client_s3_1.HeadObjectCommand({ Bucket, Key, ...options });
91
94
  this.setQueryParams(queryParams);
92
- const signedUrl = await this.getS3SignedUrlWithCustomQueryParams(command);
95
+ const signedUrl = await this.getS3SignedUrlWithCustomQueryParams(command, presignOptions);
93
96
  log.debug(`Signed HeadObject request URL: ${signedUrl}`);
94
97
  return signedUrl;
95
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cumulus/aws-client",
3
- "version": "11.1.4",
3
+ "version": "11.1.6",
4
4
  "description": "Utilities for working with AWS",
5
5
  "keywords": [
6
6
  "GIBS",
@@ -51,9 +51,9 @@
51
51
  "@aws-sdk/s3-request-presigner": "^3.53.0",
52
52
  "@aws-sdk/signature-v4-crt": "^3.53.0",
53
53
  "@aws-sdk/types": "^3.53.0",
54
- "@cumulus/checksum": "11.1.4",
55
- "@cumulus/errors": "11.1.4",
56
- "@cumulus/logger": "11.1.4",
54
+ "@cumulus/checksum": "11.1.6",
55
+ "@cumulus/errors": "11.1.6",
56
+ "@cumulus/logger": "11.1.6",
57
57
  "aws-sdk": "^2.585.0",
58
58
  "jsonpath-plus": "^1.1.0",
59
59
  "lodash": "~4.17.21",
@@ -68,5 +68,5 @@
68
68
  "devDependencies": {
69
69
  "@types/uuid": "^8.0.0"
70
70
  },
71
- "gitHead": "0c2cc0fa8bb66b2d668c95df1cc11a1a8c1d4332"
71
+ "gitHead": "cc17225ec1ded9cc2d2b1decc952da97ce0df072"
72
72
  }
package/test-utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Readable } from 'stream';
1
2
  import { AWSClientTypes } from './types';
2
3
  export declare const inTestMode: () => boolean;
3
4
  declare const localStackPorts: {
@@ -75,5 +76,6 @@ export declare function getLocalstackAwsClientOptions<T extends AWSClientTypes>(
75
76
  * @private
76
77
  */
77
78
  export declare const throttleOnce: (fn: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown;
79
+ export declare const streamToString: (stream: Readable) => Promise<unknown>;
78
80
  export {};
79
81
  //# sourceMappingURL=test-utils.d.ts.map
package/test-utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.throttleOnce = exports.getLocalstackAwsClientOptions = exports.localStackAwsClientOptions = exports.getLocalstackEndpoint = exports.inTestMode = void 0;
3
+ exports.streamToString = exports.throttleOnce = exports.getLocalstackAwsClientOptions = exports.localStackAwsClientOptions = exports.getLocalstackEndpoint = exports.inTestMode = void 0;
4
4
  const errors_1 = require("@cumulus/errors");
5
5
  const utils_1 = require("./utils");
6
6
  const inTestMode = () => process.env.NODE_ENV === 'test';
@@ -130,4 +130,13 @@ const throttleOnce = (fn) => {
130
130
  };
131
131
  };
132
132
  exports.throttleOnce = throttleOnce;
133
+ const streamToString = (stream) => {
134
+ let result = '';
135
+ // eslint-disable-next-line no-return-assign
136
+ stream.on('data', (chunk) => result += chunk.toString());
137
+ return new Promise((resolve) => {
138
+ stream.on('end', () => resolve(result));
139
+ });
140
+ };
141
+ exports.streamToString = streamToString;
133
142
  //# sourceMappingURL=test-utils.js.map