@pi-r/gcp 0.6.6 → 0.7.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/README.md CHANGED
@@ -1,10 +1,7 @@
1
- # @pi-r/gcp
1
+ ### @pi-r/gcp
2
2
 
3
- ## Documentation
3
+ https://e-mc.readthedocs.io/en/latest/cloud/gcp.html
4
4
 
5
- - [E-mc](https://e-mc.readthedocs.io/en/latest/cloud/gcp.html)
6
- - [squared](https://squared.readthedocs.io)
7
-
8
- ## LICENSE
5
+ ### LICENSE
9
6
 
10
7
  MIT
package/client/index.js CHANGED
@@ -1,18 +1,16 @@
1
1
  "use strict";
2
- exports.getProjectId = exports.createBucketRequest = exports.setPredefinedAcl = exports.setPublicRead = exports.executeBatchQuery = exports.executeQuery = exports.deleteObjectsV2 = exports.deleteObjects = exports.setBucketWebsite = exports.setBucketPolicy = exports.createBucketV2 = exports.createBucket = exports.createDatabaseClient = exports.createStorageClient = exports.validateDatabase = exports.validateStorage = exports.isFirebaseApp = void 0;
2
+ exports.CLOUD_DOWNLOAD_CHUNK = exports.CLOUD_UPLOAD_CHUNK = exports.CLOUD_UPLOAD_STREAM = exports.getProjectId = exports.createBucketRequest = exports.setPredefinedAcl = exports.setPublicRead = exports.executeBatchQuery = exports.executeQuery = exports.deleteObjectsV2 = exports.deleteObjects = exports.setBucketWebsite = exports.setBucketTagging = exports.setBucketPolicy = exports.createBucketV2 = exports.createBucket = exports.createDatabaseClient = exports.createStorageClient = exports.validateDatabase = exports.validateStorage = exports.isFirebaseApp = void 0;
3
3
  const path = require("path");
4
4
  const storage = require("@google-cloud/storage");
5
5
  const firestore_1 = require("@google-cloud/firestore");
6
6
  const util_1 = require("@e-mc/cloud/util");
7
7
  const types_1 = require("@e-mc/types");
8
- const Module = require("@e-mc/module");
9
8
  const Cloud = require("@e-mc/cloud");
10
9
  const FIREBASE_DB = {};
11
10
  const FIREBASE_STORAGE = {};
12
11
  const ADMIN_DB = {};
13
12
  const ADMIN_STORAGE = {};
14
13
  function getApp(credential, isDB) {
15
- var _a, _b;
16
14
  const { projectId, databaseURL, storageBucket, apiKey, authDomain } = credential;
17
15
  if (apiKey && authDomain) {
18
16
  const { getApps, initializeApp } = require('@firebase/app');
@@ -26,7 +24,7 @@ function getApp(credential, isDB) {
26
24
  }
27
25
  if (!app) {
28
26
  const firebaseKey = (projectId || '') + '_' + apiKey + '_' + authDomain + (isDB ? '_' + databaseURL : '');
29
- app = isDB ? FIREBASE_DB[firebaseKey] || (FIREBASE_DB[firebaseKey] = initializeApp({ ...credential, databaseURL }, firebaseKey)) : FIREBASE_STORAGE[firebaseKey] || (FIREBASE_STORAGE[firebaseKey] = initializeApp({ ...credential, storageBucket }, firebaseKey));
27
+ app = isDB ? FIREBASE_DB[firebaseKey] ||= initializeApp({ ...credential, databaseURL }, firebaseKey) : FIREBASE_STORAGE[firebaseKey] ||= initializeApp({ ...credential, storageBucket }, firebaseKey);
30
28
  }
31
29
  if (isDB) {
32
30
  const { getDatabase, goOnline } = require('@firebase/database');
@@ -47,7 +45,7 @@ function getApp(credential, isDB) {
47
45
  }
48
46
  }
49
47
  if (!app) {
50
- app = isDB ? ADMIN_DB[_a = databaseURL || '[DEFAULT]'] || (ADMIN_DB[_a] = initializeApp({ credential: applicationDefault(), databaseURL }, databaseURL)) : ADMIN_STORAGE[_b = storageBucket || '[DEFAULT]'] || (ADMIN_STORAGE[_b] = initializeApp({ credential: applicationDefault(), storageBucket }, storageBucket));
48
+ app = isDB ? ADMIN_DB[databaseURL || "[DEFAULT]"] ||= initializeApp({ credential: applicationDefault(), databaseURL }, databaseURL) : ADMIN_STORAGE[storageBucket || "[DEFAULT]"] ||= initializeApp({ credential: applicationDefault(), storageBucket }, storageBucket);
51
49
  }
52
50
  credential.admin = true;
53
51
  if (isDB) {
@@ -59,7 +57,7 @@ function getApp(credential, isDB) {
59
57
  }
60
58
  }
61
59
  async function hasBucket(client) {
62
- const [exists] = await client.exists().catch(err => isErrorConflict(err) ? [true] : [false]);
60
+ const [exists] = await client.exists().catch((err) => isErrorConflict(err) ? [true] : [false]);
63
61
  return exists;
64
62
  }
65
63
  async function updatePredefinedAcl(acl, values, metadata = {}) {
@@ -83,7 +81,7 @@ async function updatePredefinedAcl(acl, values, metadata = {}) {
83
81
  }
84
82
  if ((0, types_1.isString)(value.role)) {
85
83
  if (projectNumber !== undefined) {
86
- value.userProject || (value.userProject = projectNumber.toString());
84
+ value.userProject ||= projectNumber.toString();
87
85
  }
88
86
  return target.add(value);
89
87
  }
@@ -99,24 +97,24 @@ async function updatePredefinedAcl(acl, values, metadata = {}) {
99
97
  }
100
98
  return errors;
101
99
  })
102
- .catch(err => [err]);
100
+ .catch((err) => [err]);
103
101
  }
104
102
  async function setUniformBucketLevel(bucket, enabled) {
105
103
  return bucket.setMetadata({ iamConfiguration: { uniformBucketLevelAccess: { enabled } } });
106
104
  }
107
105
  const getFirebasePackageName = (credential) => credential.apiKey && credential.authDomain ? 'firebase' : 'firebase-admin';
108
- const hasKeyFile = (credential) => (0, types_1.isString)(credential.keyFilename || credential.keyFile || process.env.GOOGLE_APPLICATION_CREDENTIALS) || (0, types_1.isObject)(credential.credentials);
109
106
  const isErrorConflict = (err) => (err instanceof Error) && err.code === 409;
107
+ const hasGoogleAuth = (credential) => (0, types_1.isString)(credential.keyFilename || credential.keyFile || process.env.GOOGLE_APPLICATION_CREDENTIALS) || (0, types_1.isObject)(credential.credentials) || !!credential.authClient;
110
108
  function isFirebaseApp(credential, store, admin) {
111
- return !!(credential.apiKey && credential.authDomain && (store || credential.databaseURL) || credential.product === 'firebase' && process.env.GOOGLE_APPLICATION_CREDENTIALS) && (!admin || !credential.admin);
109
+ return !!(credential.apiKey && credential.authDomain && (store || credential.databaseURL) || credential.product === "firebase" && process.env.GOOGLE_APPLICATION_CREDENTIALS) && (!admin || !credential.admin);
112
110
  }
113
111
  exports.isFirebaseApp = isFirebaseApp;
114
112
  function validateStorage(credential) {
115
- return hasKeyFile(credential) || !!credential.authClient || isFirebaseApp(credential, true);
113
+ return hasGoogleAuth(credential) || isFirebaseApp(credential, true);
116
114
  }
117
115
  exports.validateStorage = validateStorage;
118
116
  function validateDatabase(credential, data) {
119
- return (hasKeyFile(credential) || !!credential.authClient) && !!(data.product || data.database || data.table || data.query || data.id || data.keys || data.kind || data.options?.query) || isFirebaseApp(credential);
117
+ return hasGoogleAuth(credential) && !!(data.product || data.database || data.table || data.query || data.id || data.keys || data.kind || data.options?.query) || isFirebaseApp(credential);
120
118
  }
121
119
  exports.validateDatabase = validateDatabase;
122
120
  function createStorageClient(credential, bucket) {
@@ -141,29 +139,26 @@ exports.createStorageClient = createStorageClient;
141
139
  function createDatabaseClient(credential, data) {
142
140
  let packageName = '';
143
141
  try {
144
- let product, name, database, table, query;
145
- if (data) {
146
- ({ product, name, table, query, database } = data);
147
- }
148
- if (product === 'firebase' || isFirebaseApp(credential)) {
142
+ if (data?.product === "firebase" || isFirebaseApp(credential)) {
149
143
  packageName = getFirebasePackageName(credential);
150
144
  return getApp(credential, true);
151
145
  }
152
146
  getProjectId(credential, true);
153
147
  if (data) {
154
- if (product === 'spanner' || !product && name && database && (query || table)) {
148
+ const { product, name, table, query, database } = data;
149
+ if (product === "spanner" || !product && name && database && (query || table)) {
155
150
  const { Spanner } = require(packageName = '@google-cloud/spanner');
156
151
  return new Spanner(credential);
157
152
  }
158
- if (product === 'bigquery' || !product && (!name && table && database || (0, types_1.isString)(query))) {
153
+ if (product === "bigquery" || !product && (!name && table && database || (0, types_1.isString)(query))) {
159
154
  const { BigQuery } = require(packageName = '@google-cloud/bigquery');
160
155
  return new BigQuery(credential);
161
156
  }
162
- if (product === 'bigtable' || !product && name && table && !database) {
157
+ if (product === "bigtable" || !product && name && table && !database) {
163
158
  const { Bigtable } = require(packageName = '@google-cloud/bigtable');
164
159
  return new Bigtable(credential);
165
160
  }
166
- if (product === 'datastore' || !product && (data.keys || data.kind || query && !table && !data.id)) {
161
+ if (product === "datastore" || !product && (data.keys || data.kind || query && !table && !data.id)) {
167
162
  const { Datastore } = require(packageName = '@google-cloud/datastore');
168
163
  return new Datastore(credential);
169
164
  }
@@ -222,32 +217,32 @@ async function setBucketPolicy(credential, bucket, options, service = 'gcp') {
222
217
  if (!Array.isArray(options.acl)) {
223
218
  options.acl = undefined;
224
219
  switch (acl) {
225
- case 'private':
226
- case 'projectPrivate':
227
- await client.makePrivate(options).catch(err => !isErrorConflict(err) && errors.push(err));
228
- await setPredefinedAcl.call(this, client.acl, acl, client, '', options.bucket?.metadata).catch(err => errors.push(err));
220
+ case "private":
221
+ case "projectPrivate":
222
+ await client.makePrivate(options).catch((err) => !isErrorConflict(err) && errors.push(err));
223
+ await setPredefinedAcl.call(this, client.acl, acl, client, '', options.bucket?.metadata).catch((err) => errors.push(err));
229
224
  break;
230
- case 'authenticatedRead':
231
- await client.makePublic(options).catch(err => !isErrorConflict(err) && errors.push(err));
225
+ case "authenticatedRead":
226
+ await client.makePublic(options).catch((err) => !isErrorConflict(err) && errors.push(err));
232
227
  options = { ...options };
233
228
  options.acl = [{ entity: 'allUsers' }, { entity: 'allAuthenticatedUsers', role: 'READER' }];
234
229
  break;
235
- case 'publicReadWrite':
236
- await client.makePublic(options).catch(err => !isErrorConflict(err) && errors.push(err));
230
+ case "publicReadWrite":
231
+ await client.makePublic(options).catch((err) => !isErrorConflict(err) && errors.push(err));
237
232
  options = { ...options };
238
233
  options.acl = [{ entity: 'allUsers', role: 'WRITER' }];
239
234
  break;
240
- case 'publicRead':
241
- await client.makePublic(options).catch(err => !isErrorConflict(err) && errors.push(err));
235
+ case "publicRead":
236
+ await client.makePublic(options).catch((err) => !isErrorConflict(err) && errors.push(err));
242
237
  break;
243
238
  case 'bucketAccessUniform':
244
- await setUniformBucketLevel(client, true).catch(err => errors.push(err));
239
+ await setUniformBucketLevel(client, true).catch((err) => errors.push(err));
245
240
  break;
246
241
  case 'bucketAccessACL':
247
- await setUniformBucketLevel(client, false).catch(err => errors.push(err));
242
+ await setUniformBucketLevel(client, false).catch((err) => errors.push(err));
248
243
  break;
249
244
  default:
250
- errors.push((0, types_1.errorValue)('Invalid ACL', Module.asString(acl) || 'empty'));
245
+ errors.push((0, types_1.errorValue)('Invalid ACL', Cloud.asString(acl) || 'empty'));
251
246
  break;
252
247
  }
253
248
  }
@@ -262,6 +257,33 @@ async function setBucketPolicy(credential, bucket, options, service = 'gcp') {
262
257
  return true;
263
258
  }
264
259
  exports.setBucketPolicy = setBucketPolicy;
260
+ async function setBucketTagging(credential, bucket, labels, service = 'gcp') {
261
+ if (isFirebaseApp(credential, true, true) || !(0, types_1.isPlainObject)(labels)) {
262
+ return false;
263
+ }
264
+ const client = createStorageClient.call(this, credential, bucket).bucket(bucket);
265
+ const deleting = Object.keys(labels).length === 0;
266
+ if (deleting) {
267
+ const [metadata] = await client.getMetadata();
268
+ if (!(0, types_1.isPlainObject)(metadata.labels)) {
269
+ return true;
270
+ }
271
+ labels = {};
272
+ for (const key in metadata.labels) {
273
+ labels[key] = null;
274
+ }
275
+ }
276
+ return client.setMetadata({ labels })
277
+ .then(([metadata]) => {
278
+ this.formatMessage(64, service, [deleting ? "Tags deleted" : "Tags created", bucket], ((0, types_1.isPlainObject)(metadata.labels) ? Object.keys(metadata.labels).length : 0) + ' labels (current)', { ...Cloud.LOG_CLOUD_COMMAND });
279
+ return true;
280
+ })
281
+ .catch((err) => {
282
+ this.formatFail(64, service, ["Unable to update bucket tagging", bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
283
+ return false;
284
+ });
285
+ }
286
+ exports.setBucketTagging = setBucketTagging;
265
287
  async function setBucketWebsite(credential, bucket, options, service = 'gcp') {
266
288
  if (isFirebaseApp(credential, true, true)) {
267
289
  return false;
@@ -276,11 +298,11 @@ async function setBucketWebsite(credential, bucket, options, service = 'gcp') {
276
298
  }
277
299
  return createStorageClient.call(this, credential, bucket).bucket(bucket).setMetadata({ website })
278
300
  .then(() => {
279
- this.formatMessage(64, service, ["Bucket configured", bucket], website, { ...Cloud.LOG_CLOUD_COMMAND });
301
+ this.formatMessage(64, service, ["Bucket website configured", bucket], website, { ...Cloud.LOG_CLOUD_COMMAND });
280
302
  return true;
281
303
  })
282
- .catch(err => {
283
- this.formatFail(64, service, ["Unable to configure bucket", bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
304
+ .catch((err) => {
305
+ this.formatFail(64, service, ["Unable to set bucket website", bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
284
306
  return false;
285
307
  });
286
308
  }
@@ -304,7 +326,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
304
326
  }
305
327
  if (recursive && prefixes.length) {
306
328
  for (const { name } of prefixes) {
307
- await recurse(Module.joinPath(prefix, name));
329
+ await recurse(Cloud.joinPath(prefix, name));
308
330
  }
309
331
  }
310
332
  }
@@ -327,7 +349,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
327
349
  const result = await client.getFiles();
328
350
  for (const { name } of result[0]) {
329
351
  if (!name.includes('/')) {
330
- tasks.push(client.file(name).delete({ ignoreNotFound: true }).catch(err => this.formatFail(64, service, ["Delete failed", name], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false })));
352
+ tasks.push(client.file(name).delete({ ignoreNotFound: true }).catch((err) => this.formatFail(64, service, ["Delete failed", name], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false })));
331
353
  }
332
354
  }
333
355
  }
@@ -354,7 +376,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
354
376
  this.formatMessage(64, service, ["Bucket emptied" + ` (${recursive ? 'recursive' : files})`, bucket], recursive ? files : '', { ...Cloud.LOG_CLOUD_COMMAND });
355
377
  }
356
378
  })
357
- .catch(err => {
379
+ .catch((err) => {
358
380
  errors.push(err);
359
381
  });
360
382
  }
@@ -368,7 +390,7 @@ exports.executeQuery = executeQuery;
368
390
  async function executeBatchQuery(credential, batch, sessionKey) {
369
391
  const length = batch.length;
370
392
  const result = new Array(length);
371
- const clients = [];
393
+ const clients = {};
372
394
  const caching = length > 0 && this.hasCache(batch[0].service, sessionKey);
373
395
  const cacheValue = { value: this.valueOfKey(credential, 'cache'), sessionKey };
374
396
  const authItems = [];
@@ -383,7 +405,14 @@ async function executeBatchQuery(credential, batch, sessionKey) {
383
405
  };
384
406
  const closeClient = () => {
385
407
  const fs = clients[3];
386
- fs?.terminate();
408
+ if (fs) {
409
+ fs.terminate();
410
+ return;
411
+ }
412
+ const sp = clients[5];
413
+ if (sp) {
414
+ sp.close();
415
+ }
387
416
  };
388
417
  const getFamily = (data, row, family) => {
389
418
  const familyData = data[family];
@@ -416,14 +445,14 @@ async function executeBatchQuery(credential, batch, sessionKey) {
416
445
  };
417
446
  cacheValue.exclusiveOf = Array.isArray(ignoreCache) ? ignoreCache : undefined;
418
447
  let rows, queryString = '';
419
- if (product === 'firebase' || isFirebaseApp(credential)) {
448
+ if (product === "firebase" || isFirebaseApp(credential)) {
420
449
  if (!(0, types_1.isString)(query)) {
421
450
  closeClient();
422
451
  throw (0, util_1.formatError)(item, "Missing database query");
423
452
  }
424
453
  const orderBy = item.orderBy;
425
454
  if (useCache) {
426
- queryString = query + Module.asString(orderBy, true);
455
+ queryString = query + Cloud.asString(orderBy, true);
427
456
  if (!update && (rows = getCache(queryString))) {
428
457
  result[i] = rows;
429
458
  continue;
@@ -472,7 +501,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
472
501
  rows = [group && (0, types_1.isObject)(val) && (0, types_1.isObject)(val[group[0]]) ? val[group[0]] : val];
473
502
  }
474
503
  }
475
- else if (product === 'spanner' || !product && name && database && (query || table)) {
504
+ else if (product === "spanner" || !product && name && database && (query || table)) {
476
505
  const columns = (0, types_1.isPlainObject)(query) && query.columns || item.columns;
477
506
  if (!(query || table && columns)) {
478
507
  closeClient();
@@ -484,7 +513,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
484
513
  }
485
514
  const { updateType, options = {} } = item;
486
515
  if (useCache) {
487
- queryString = name + '_' + database + '_' + (table ? table + (columns ? Module.asString(columns, true) : '') : '') + Module.asString(query, true) + (limit > 0 ? limit : '');
516
+ queryString = name + '_' + database + '_' + (table ? table + (columns ? Cloud.asString(columns, true) : '') : '') + Cloud.asString(query, true) + (limit > 0 ? limit : '');
488
517
  if (!update && (rows = getCache(queryString))) {
489
518
  result[i] = rows;
490
519
  continue;
@@ -515,8 +544,12 @@ async function executeBatchQuery(credential, batch, sessionKey) {
515
544
  }
516
545
  else {
517
546
  transaction.runUpdate(update)
518
- .then(async () => transaction.commit().then(() => resolve()).catch(error => reject(error)))
519
- .catch(error => reject(error));
547
+ .then(async () => {
548
+ transaction.commit()
549
+ .then(() => resolve())
550
+ .catch(reject);
551
+ })
552
+ .catch(reject);
520
553
  }
521
554
  });
522
555
  });
@@ -524,12 +557,12 @@ async function executeBatchQuery(credential, batch, sessionKey) {
524
557
  const request = (0, types_1.isPlainObject)(query) ? query : { sql: query };
525
558
  request.json = true;
526
559
  if (limit > 0) {
527
- (request.gaxOptions || (request.gaxOptions = {})).maxResults = limit;
560
+ (request.gaxOptions ||= {}).maxResults = limit;
528
561
  }
529
562
  [rows] = await db.run(request);
530
563
  }
531
564
  }
532
- else if (product === 'bigtable' || !product && name && table && !database) {
565
+ else if (product === "bigtable" || !product && name && table && !database) {
533
566
  if (!name || !table) {
534
567
  closeClient();
535
568
  throw (0, util_1.formatError)(item, !name ? "Missing database name" : "Missing database table");
@@ -539,10 +572,10 @@ async function executeBatchQuery(credential, batch, sessionKey) {
539
572
  options.filter = query;
540
573
  }
541
574
  if (!id && limit > 0) {
542
- (options.gaxOptions || (options.gaxOptions = {})).maxResults = limit;
575
+ (options.gaxOptions ||= {}).maxResults = limit;
543
576
  }
544
577
  if (useCache) {
545
- queryString = name + '_' + table + '_' + ((0, types_1.isString)(id) ? id + ((0, types_1.isArray)(columns) ? '_' + Module.asString(columns, true) : '') : '') + Module.asString(options, true);
578
+ queryString = name + '_' + table + '_' + ((0, types_1.isString)(id) ? id + ((0, types_1.isArray)(columns) ? '_' + Cloud.asString(columns, true) : '') : '') + Cloud.asString(options, true);
546
579
  if (!(update && id) && (rows = getCache(queryString))) {
547
580
  result[i] = rows;
548
581
  continue;
@@ -573,7 +606,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
573
606
  });
574
607
  }
575
608
  }
576
- else if (product === 'bigquery' || !product && (!name && database && table || (0, types_1.isString)(query) || (0, types_1.isString)(item.options?.query))) {
609
+ else if (product === "bigquery" || !product && (!name && database && table || (0, types_1.isString)(query) || (0, types_1.isString)(item.options?.query))) {
577
610
  const { params, options = {} } = item;
578
611
  if ((0, types_1.isString)(query)) {
579
612
  options.query = query;
@@ -585,7 +618,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
585
618
  options.maxResults = limit;
586
619
  }
587
620
  if (useCache) {
588
- queryString = query + (database ? '_' + database + (table ? '_' + table : '') : '') + Module.asString(options, true);
621
+ queryString = query + (database ? '_' + database + (table ? '_' + table : '') : '') + Cloud.asString(options, true);
589
622
  if (!update && (rows = getCache(queryString))) {
590
623
  result[i] = rows;
591
624
  continue;
@@ -612,7 +645,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
612
645
  }
613
646
  [rows] = await job.getQueryResults();
614
647
  }
615
- else if (product === 'datastore' || !product && (item.keys || item.kind || query && !table && !id)) {
648
+ else if (product === "datastore" || !product && (item.keys || item.kind || query && !table && !id)) {
616
649
  const { keys, kind } = item;
617
650
  if (!keys && !kind && !Array.isArray(query)) {
618
651
  closeClient();
@@ -668,19 +701,19 @@ async function executeBatchQuery(credential, batch, sessionKey) {
668
701
  db.runQuery(task.select('__key__'), item.options).then(entities => {
669
702
  if ((0, types_1.isArray)(entities)) {
670
703
  const items = entities.map((entity) => db.key(entity[db.KEY]));
671
- db.save({ key: items, data: update }).then(() => resolve()).catch(err => reject(err));
704
+ db.save({ key: items, data: update }).then(() => resolve()).catch((err) => reject(err));
672
705
  }
673
706
  else {
674
707
  resolve();
675
708
  }
676
709
  })
677
- .catch(err => reject(err));
710
+ .catch((err) => reject(err));
678
711
  });
679
712
  }
680
713
  [rows] = await db.runQuery(task, item.options);
681
714
  }
682
715
  }
683
- else if (product === 'firestore' || !product && table) {
716
+ else if (product === "firestore" || !product && table) {
684
717
  if (!table) {
685
718
  closeClient();
686
719
  throw (0, util_1.formatError)(item, "Missing database table");
@@ -704,7 +737,34 @@ async function executeBatchQuery(credential, batch, sessionKey) {
704
737
  else {
705
738
  const doc = db.collection(table).doc(id);
706
739
  if ((0, types_1.isPlainObject)(update)) {
707
- await doc.update(update, { exists: true });
740
+ const pattern = /^__(delete|serverTimestamp|increment)(?:<(.+)>)?__$/;
741
+ for (const attr in update) {
742
+ const match = pattern.exec(attr);
743
+ if (match) {
744
+ const method = match[1];
745
+ switch (method) {
746
+ case 'delete':
747
+ case 'serverTimestamp':
748
+ update[attr] = firestore_1.FieldValue[method]();
749
+ break;
750
+ case 'increment':
751
+ update[attr] = firestore_1.FieldValue.increment(match[2] ? +match[2] : 1);
752
+ break;
753
+ }
754
+ }
755
+ }
756
+ switch (item.updateType) {
757
+ case 1:
758
+ await doc.create(update);
759
+ break;
760
+ case 2:
761
+ case 3:
762
+ await doc.set(update, { merge: item.updateType === 3 });
763
+ break;
764
+ default:
765
+ await doc.update(update);
766
+ break;
767
+ }
708
768
  }
709
769
  const data = (await doc.get()).data();
710
770
  if (data) {
@@ -714,7 +774,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
714
774
  }
715
775
  else if (Array.isArray(query)) {
716
776
  const orderBy = item.orderBy;
717
- if (useCache && (rows = getCache(queryString = table + '_' + Module.asString(query, true) + Module.asString(orderBy, true) + (limit > 0 ? limit : '')))) {
777
+ if (useCache && (rows = getCache(queryString = table + '_' + Cloud.asString(query, true) + Cloud.asString(orderBy, true) + (limit > 0 ? limit : '')))) {
718
778
  result[i] = rows;
719
779
  continue;
720
780
  }
@@ -726,7 +786,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
726
786
  switch (method) {
727
787
  case 'whereAnd':
728
788
  case 'whereOr':
729
- if (params.every(p => Array.isArray(p))) {
789
+ if (params.every(p => (0, types_1.isArray)(p))) {
730
790
  db = db.where(firestore_1.Filter[method === 'whereAnd' ? 'and' : 'or'](...params.map((p) => firestore_1.Filter.where(p[0], p[1], p[2]))));
731
791
  }
732
792
  else {
@@ -783,7 +843,7 @@ function setPublicRead(acl, pathname, requested, service = 'gcp') {
783
843
  this.formatMessage(64, service, "Grant public-read", pathname, { ...Cloud.LOG_CLOUD_COMMAND });
784
844
  }
785
845
  })
786
- .catch(err => {
846
+ .catch((err) => {
787
847
  if (requested) {
788
848
  this.formatMessage(64, service, ["Unable to grant public-read", pathname], err, { ...Cloud.LOG_CLOUD_WARN });
789
849
  }
@@ -815,7 +875,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
815
875
  items = [{ entity: 'allUsers' }, { entity: 'allAuthenticatedUsers' }];
816
876
  }
817
877
  switch (value) {
818
- case 'private': {
878
+ case "private": {
819
879
  const owners = new Set(acl.filter(item => item.role === 'OWNER' && item.entity).map(item => item.entity));
820
880
  if (owner) {
821
881
  owners.add(owner);
@@ -830,7 +890,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
830
890
  }
831
891
  break;
832
892
  }
833
- case 'projectPrivate':
893
+ case "projectPrivate":
834
894
  for (const user of acl) {
835
895
  const entity = user.entity;
836
896
  if (entity) {
@@ -839,7 +899,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
839
899
  }
840
900
  }
841
901
  break;
842
- case 'bucketOwnerRead':
902
+ case "bucketOwnerRead":
843
903
  if (bucket && owner) {
844
904
  for (const user of acl) {
845
905
  const entity = user.entity;
@@ -848,7 +908,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
848
908
  }
849
909
  }
850
910
  }
851
- case 'bucketOwnerFullControl':
911
+ case "bucketOwnerFullControl":
852
912
  if (bucket && owner) {
853
913
  items.push({ entity: owner, role: 'OWNER' });
854
914
  }
@@ -915,13 +975,16 @@ function getProjectId(credential, cache) {
915
975
  const id = require(pathname).project_id;
916
976
  if ((0, types_1.isString)(id)) {
917
977
  projectId = id;
918
- credential.projectId = projectId;
919
978
  }
920
979
  }
921
980
  catch {
922
981
  }
982
+ credential.projectId = projectId || (projectId = process.env.GCLOUD_PROJECT);
923
983
  }
924
984
  }
925
985
  return projectId;
926
986
  }
927
987
  exports.getProjectId = getProjectId;
988
+ exports.CLOUD_UPLOAD_STREAM = true;
989
+ exports.CLOUD_UPLOAD_CHUNK = true;
990
+ exports.CLOUD_DOWNLOAD_CHUNK = true;
package/download/index.js CHANGED
@@ -3,20 +3,18 @@ const path = require("path");
3
3
  const fs = require("fs");
4
4
  const https = require("https");
5
5
  const types_1 = require("@e-mc/types");
6
- const Module = require("@e-mc/module");
7
6
  const Cloud = require("@e-mc/cloud");
8
7
  const client_1 = require("@pi-r/gcp");
9
8
  module.exports = function download(credential, service = 'gcp') {
10
9
  const storage = client_1.createStorageClient.call(this, credential);
11
10
  return (data, callback) => {
12
11
  const { bucket: Bucket, download: target } = data;
13
- const Key = target.filename;
14
- const firebase = (0, client_1.isFirebaseApp)(credential, true, true);
12
+ const Key = target.keyname || target.filename;
15
13
  let tempDir;
16
- const downloadResponse = (err, url) => {
14
+ const downloadResponse = (err, url = null) => {
17
15
  callback(err, url);
18
16
  if (tempDir) {
19
- queueMicrotask(() => Module.removeDir(tempDir));
17
+ queueMicrotask(() => Cloud.removeDir(tempDir));
20
18
  }
21
19
  };
22
20
  if (!Bucket || !Key) {
@@ -24,13 +22,13 @@ module.exports = function download(credential, service = 'gcp') {
24
22
  return;
25
23
  }
26
24
  if (!(tempDir = this.getTempDir({ uuidDir: true }))) {
27
- downloadResponse(new Error("Unable to create temp directory"));
25
+ downloadResponse((0, types_1.errorValue)("Unable to create temp directory", service));
28
26
  return;
29
27
  }
30
- const location = Module.joinPath(Bucket, Key);
31
- const destination = path.join(tempDir, path.basename(Key));
28
+ const location = Cloud.joinPath(Bucket, Key);
29
+ const destination = path.join(tempDir, path.basename(target.filename || Key));
32
30
  const deleteMessage = () => this.formatMessage(64, service, "Delete success", location, { ...Cloud.LOG_CLOUD_DELETE });
33
- if (firebase) {
31
+ if ((0, client_1.isFirebaseApp)(credential, true, true)) {
34
32
  const { ref, getDownloadURL, deleteObject } = require('@firebase/storage');
35
33
  const objectRef = ref(storage, Key);
36
34
  getDownloadURL(objectRef).then(url => {
@@ -44,7 +42,7 @@ module.exports = function download(credential, service = 'gcp') {
44
42
  if (target.deleteObject) {
45
43
  deleteObject(objectRef)
46
44
  .then(() => deleteMessage())
47
- .catch(err => this.formatFail(64, service, ["Delete failed", location], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active }));
45
+ .catch((err) => this.formatFail(64, service, ["Delete failed", location], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active }));
48
46
  }
49
47
  })
50
48
  .on('error', err => downloadResponse(err)));
@@ -54,26 +52,36 @@ module.exports = function download(credential, service = 'gcp') {
54
52
  }
55
53
  });
56
54
  });
55
+ return;
57
56
  }
58
- else {
59
- const bucket = storage.bucket(Bucket);
60
- const file = bucket.file(Key, { generation: target.versionId });
61
- file.download({ destination })
62
- .then(() => {
63
- downloadResponse(null, destination);
64
- const deleteObject = target.deleteObject;
65
- if (deleteObject) {
66
- file.delete((0, types_1.isPlainObject)(deleteObject) ? deleteObject : { ignoreNotFound: true }, err => {
67
- if (!err) {
68
- deleteMessage();
69
- }
70
- else {
71
- this.formatFail(64, service, ["Delete failed", location], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active });
72
- }
73
- });
74
- }
75
- })
76
- .catch(err => downloadResponse(err));
57
+ const { flags = 0 } = target;
58
+ const bucket = storage.bucket(Bucket);
59
+ const file = bucket.file(Key, { generation: target.versionId });
60
+ const chunkSize = flags & 4 ? (0, types_1.alignSize)(target.chunkSize, 1024) : 0;
61
+ let manager;
62
+ if (chunkSize > 0) {
63
+ const { TransferManager } = require('@google-cloud/storage');
64
+ let concurrencyLimit = target.chunkLimit;
65
+ if (!concurrencyLimit && target.options) {
66
+ ({ concurrencyLimit } = target.options);
67
+ }
68
+ manager = new TransferManager(bucket).downloadFileInChunks(file, { destination, chunkSizeBytes: chunkSize, concurrencyLimit });
77
69
  }
70
+ (manager || file.download({ destination }))
71
+ .then(() => {
72
+ downloadResponse(null, destination);
73
+ const deleteObject = target.deleteObject;
74
+ if (deleteObject) {
75
+ file.delete((0, types_1.isObject)(deleteObject) ? deleteObject : { ignoreNotFound: true }, err => {
76
+ if (!err) {
77
+ deleteMessage();
78
+ }
79
+ else {
80
+ this.formatFail(64, service, ["Delete failed", location], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active });
81
+ }
82
+ });
83
+ }
84
+ })
85
+ .catch((err) => downloadResponse(err));
78
86
  };
79
87
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-r/gcp",
3
- "version": "0.6.6",
3
+ "version": "0.7.0",
4
4
  "description": "Google (GCP) cloud functions for E-mc.",
5
5
  "main": "client/index.js",
6
6
  "publishConfig": {
@@ -20,10 +20,10 @@
20
20
  "license": "MIT",
21
21
  "homepage": "https://github.com/anpham6/pi-r#readme",
22
22
  "dependencies": {
23
- "@e-mc/cloud": "^0.8.7",
24
- "@e-mc/module": "^0.8.7",
25
- "@e-mc/types": "^0.8.7",
23
+ "@e-mc/cloud": "^0.9.0",
24
+ "@e-mc/module": "^0.9.0",
25
+ "@e-mc/types": "^0.9.0",
26
26
  "@google-cloud/firestore": "^7.6.0",
27
- "@google-cloud/storage": "^7.9.0"
27
+ "@google-cloud/storage": "^7.10.1"
28
28
  }
29
29
  }
@@ -0,0 +1,36 @@
1
+ import type { CloudDatabase, CloudStorage } from '@e-mc/types/lib/cloud';
2
+
3
+ import type { StorageOptions } from '@google-cloud/storage';
4
+ import type { PathType } from '@google-cloud/datastore';
5
+ import type { entity } from '@google-cloud/datastore/build/src/entity';
6
+ import type { FirebaseOptions as IFirebaseOptions } from '@firebase/app';
7
+
8
+ // @ts-ignore
9
+ import type { GoogleAuthOptions } from 'google-auth-library';
10
+
11
+ export type GCPStorage = CloudStorage<GCPStorageCredential, "gcp | gcloud">;
12
+
13
+ export interface GCPStorageCredential extends StorageOptions, IFirebaseOptions {}
14
+
15
+ export interface GCPDatabaseQuery<T = PlainObject> extends Omit<CloudDatabase<unknown, T, unknown, unknown[] | StandardMap, GCPDatabaseCredential>, "id"> {
16
+ source: "cloud";
17
+ service: "gcp" | "gcloud";
18
+ product?: "firestore" | "firebase" | "bigquery" | "bigtable" | "datastore" | "spanner";
19
+ database?: string;
20
+ id?: ArrayOf<string>;
21
+ updateType?: 0 | 1 | 2 | 3;
22
+ columns?: string[];
23
+ keys?: ArrayOf<DatastoreKey>;
24
+ kind?: ArrayOf<string>;
25
+ orderBy?: unknown[][];
26
+ }
27
+
28
+ export type GCPDatabaseCredential = GoogleAuthOptions;
29
+
30
+ export interface FirebaseOptions extends IFirebaseOptions {
31
+ product?: "firebase";
32
+ admin?: boolean;
33
+ }
34
+
35
+ export type DatastoreKey = string | PathType[] | entity.KeyOptions;
36
+ export type BucketAcl = "authenticatedRead" | "private" | "projectPrivate" | "publicRead" | "publicReadWrite" | "bucketAccessUniform" | "bucketAccessACL";
package/upload/index.js CHANGED
@@ -1,21 +1,21 @@
1
1
  "use strict";
2
2
  const path = require("path");
3
+ const fs = require("fs");
4
+ const stream = require("stream");
3
5
  const util_1 = require("@e-mc/cloud/util");
4
6
  const types_1 = require("@e-mc/types");
5
- const Module = require("@e-mc/module");
6
7
  const Cloud = require("@e-mc/cloud");
7
8
  const client_1 = require("@pi-r/gcp");
8
9
  const BUCKET_SESSION = new Set();
9
10
  const BUCKET_RESPONSE = {};
10
- const getBucketKey = (credential, bucket, acl = '') => Module.asString(credential, true) + bucket + '_' + acl;
11
+ const getBucketKey = (credential, bucket, acl = '') => Cloud.asString(credential, true) + bucket + '_' + acl;
11
12
  module.exports = function upload(credential, service = 'gcp') {
12
13
  const storage = client_1.createStorageClient.call(this, credential);
13
14
  return async (data, callback) => {
14
- var _a;
15
15
  const firebase = (0, client_1.isFirebaseApp)(credential, true, true);
16
16
  const { bucket, localUri } = data;
17
- const { pathname = '', fileGroup, contentType, metadata, endpoint, active, publicRead, acl, admin = {}, overwrite, options } = data.upload;
18
- let filename = data.upload.filename || path.basename(localUri), bucketClient, bucketKey, listFiles;
17
+ const { pathname = '', flags = 0, fileGroup, contentType, metadata, endpoint, active, publicRead, acl, admin = {}, overwrite, options } = data.upload;
18
+ let filename = data.upload.filename || path.basename(localUri), chunkSizeBytes = 0, bucketClient, bucketKey, listFiles;
19
19
  const uploadResponse = (err, url) => {
20
20
  if (!firebase) {
21
21
  BUCKET_SESSION.delete(bucket);
@@ -42,8 +42,8 @@ module.exports = function upload(credential, service = 'gcp') {
42
42
  else {
43
43
  const configBucket = admin.configBucket;
44
44
  if (!BUCKET_SESSION.has(bucket)) {
45
- const bucketAcl = admin.publicRead ? 'publicRead' : admin.acl;
46
- const response = BUCKET_RESPONSE[_a = bucketKey = getBucketKey(credential, bucket, bucketAcl)] || (BUCKET_RESPONSE[_a] = client_1.createBucketV2.call(this, credential, bucket, bucketAcl, configBucket?.create || (0, client_1.createBucketRequest)(credential)));
45
+ const bucketAcl = admin.publicRead ? "publicRead" : admin.acl;
46
+ const response = BUCKET_RESPONSE[bucketKey = getBucketKey(credential, bucket, bucketAcl)] ||= client_1.createBucketV2.call(this, credential, bucket, bucketAcl, configBucket?.create);
47
47
  if (!await response) {
48
48
  uploadResponse(null);
49
49
  return;
@@ -51,13 +51,49 @@ module.exports = function upload(credential, service = 'gcp') {
51
51
  BUCKET_SESSION.add(bucket);
52
52
  }
53
53
  bucketClient = storage.bucket(bucket);
54
- const retentionPolicy = configBucket?.retentionPolicy;
55
- if (typeof retentionPolicy === 'number') {
56
- bucketClient.setRetentionPeriod(retentionPolicy)
57
- .then(([response]) => {
58
- this.formatMessage(64, service, ["Bucket configured" + ' (retention policy)', bucket], response.retentionPolicy?.effectiveTime, { ...Cloud.LOG_CLOUD_COMMAND });
59
- })
60
- .catch(err => addLog(err));
54
+ if (configBucket) {
55
+ let { cors, lifecycle, retentionPolicy } = configBucket;
56
+ const commandMessage = (feature, message) => this.formatMessage(64, service, ["Bucket configured" + ` (${feature})`, bucket], message, { ...Cloud.LOG_CLOUD_COMMAND });
57
+ if (typeof retentionPolicy === 'number') {
58
+ bucketClient.setRetentionPeriod(retentionPolicy)
59
+ .then(([response]) => commandMessage('Retention Policy', response.retentionPolicy?.effectiveTime))
60
+ .catch((err) => addLog(err));
61
+ }
62
+ if ((0, types_1.isObject)(cors)) {
63
+ bucketClient.setCorsConfiguration(Array.isArray(cors) ? cors : [cors])
64
+ .then(([response]) => {
65
+ const length = response.cors?.length;
66
+ commandMessage('CORS', length ? length + ' rules' : null);
67
+ })
68
+ .catch((err) => addLog(err));
69
+ }
70
+ if ((0, types_1.isObject)(lifecycle)) {
71
+ let append;
72
+ if (Array.isArray(lifecycle)) {
73
+ if (lifecycle.length === 0) {
74
+ append = false;
75
+ }
76
+ else {
77
+ const arg = lifecycle[lifecycle.length - 1];
78
+ if (typeof arg === 'boolean') {
79
+ append = arg;
80
+ lifecycle = lifecycle.slice(0, lifecycle.length - 1);
81
+ }
82
+ }
83
+ }
84
+ bucketClient.addLifecycleRule(lifecycle, { append })
85
+ .then(([response]) => {
86
+ const length = response.lifecycle?.rule?.length;
87
+ commandMessage('Lifecycle', length ? length + ' rules' : null);
88
+ })
89
+ .catch((err) => addLog(err));
90
+ }
91
+ }
92
+ if (flags & 4) {
93
+ const chunkSize = (0, types_1.alignSize)(data.upload.chunkSize, 1024);
94
+ if (chunkSize > 0) {
95
+ chunkSizeBytes = Math.max(chunkSize, 8388608);
96
+ }
61
97
  }
62
98
  }
63
99
  if (!overwrite && (!listFiles || listFiles.length)) {
@@ -71,7 +107,7 @@ module.exports = function upload(credential, service = 'gcp') {
71
107
  break;
72
108
  }
73
109
  }
74
- const name = pathname ? Module.joinPath(pathname, filename) : filename;
110
+ const name = pathname ? Cloud.joinPath(pathname, filename) : filename;
75
111
  if (firebase) {
76
112
  exists = listFiles.includes(name);
77
113
  }
@@ -83,16 +119,94 @@ module.exports = function upload(credential, service = 'gcp') {
83
119
  this.formatMessage(64, service, ["File renamed", current], filename, { ...Cloud.LOG_CLOUD_WARN });
84
120
  }
85
121
  }
86
- const getURL = (value) => endpoint ? Module.joinPath(endpoint, value) : Module.joinPath("https://storage.googleapis.com", bucket, value);
122
+ const getURL = (value) => endpoint ? Cloud.joinPath(endpoint, value) : Cloud.joinPath("https://storage.googleapis.com", bucket, value);
87
123
  const Key = [filename];
88
124
  const Body = [data.buffer];
125
+ const Path = [];
126
+ const Stream = [];
89
127
  const ContentType = [contentType];
90
- if (fileGroup) {
91
- const [key, body, type] = (0, util_1.createKeyAndBody)(filename, fileGroup, addLog);
128
+ if (chunkSizeBytes > 0) {
129
+ const main = Body[0];
130
+ if (main.length > chunkSizeBytes) {
131
+ try {
132
+ fs.writeFileSync(localUri, main);
133
+ Path[0] = localUri;
134
+ }
135
+ catch {
136
+ if (flags & 2) {
137
+ Stream[0] = stream.Readable.from(main, { highWaterMark: chunkSizeBytes });
138
+ }
139
+ }
140
+ }
141
+ else if (main.length === 0) {
142
+ try {
143
+ if (fs.statSync(localUri).size > chunkSizeBytes) {
144
+ Path[0] = localUri;
145
+ }
146
+ else if (flags & 2) {
147
+ Stream[0] = fs.createReadStream(localUri, { highWaterMark: chunkSizeBytes, signal: this.signal });
148
+ }
149
+ else {
150
+ Body[0] = fs.readFileSync(localUri);
151
+ }
152
+ }
153
+ catch (err) {
154
+ uploadResponse(err);
155
+ return;
156
+ }
157
+ }
158
+ if (fileGroup) {
159
+ const [key, body, type] = (0, util_1.createKeyAndBody)(filename, fileGroup, chunkSizeBytes, addLog, flags);
160
+ Key.push(...key);
161
+ ContentType.push(...type);
162
+ for (let i = 0; i < body.length; ++i) {
163
+ const item = body[i];
164
+ if (typeof item === 'string') {
165
+ Path[i + 1] = item;
166
+ }
167
+ else if (Buffer.isBuffer(item)) {
168
+ Body[i + 1] = item;
169
+ }
170
+ else {
171
+ Stream[i + 1] = item;
172
+ }
173
+ }
174
+ }
175
+ }
176
+ else if ((flags & 2) && !firebase) {
177
+ try {
178
+ Stream.push(data.buffer.length ? stream.Readable.from(data.buffer) : fs.createReadStream(localUri, { signal: this.signal }));
179
+ if (fileGroup) {
180
+ const [key, body, type] = (0, util_1.createKeyAndBody)(filename, fileGroup, 0, addLog, 2);
181
+ Key.push(...key);
182
+ Stream.push(...body);
183
+ ContentType.push(...type);
184
+ }
185
+ }
186
+ catch (err) {
187
+ uploadResponse(err);
188
+ return;
189
+ }
190
+ }
191
+ else if (fileGroup) {
192
+ const [key, body, type] = (0, util_1.createKeyAndBody)(filename, fileGroup, 0, addLog);
92
193
  Key.push(...key);
93
194
  Body.push(...body);
94
195
  ContentType.push(...type);
95
196
  }
197
+ let headers, concurrencyLimit, maxQueueSize;
198
+ if (options) {
199
+ ({ headers, concurrencyLimit, maxQueueSize } = options);
200
+ if (headers !== undefined) {
201
+ delete options.headers;
202
+ }
203
+ if (concurrencyLimit !== undefined) {
204
+ delete options.concurrencyLimit;
205
+ }
206
+ if (maxQueueSize !== undefined) {
207
+ delete options.maxQueueSize;
208
+ }
209
+ }
96
210
  for (let i = 0; i < Key.length; ++i) {
97
211
  const first = i === 0;
98
212
  if (this.aborted) {
@@ -101,12 +215,21 @@ module.exports = function upload(credential, service = 'gcp') {
101
215
  }
102
216
  return;
103
217
  }
104
- const destination = pathname ? Module.joinPath(pathname, Key[i]) : Key[i];
218
+ const destination = pathname ? Cloud.joinPath(pathname, Key[i]) : Key[i];
105
219
  if (firebase) {
106
220
  const { ref, uploadBytes } = require('@firebase/storage');
107
221
  const params = { ...options };
108
222
  if (first) {
109
- params.contentType || (params.contentType = ContentType[i]);
223
+ if (Body[0].length === 0) {
224
+ try {
225
+ Body[0] = fs.readFileSync(localUri);
226
+ }
227
+ catch (err) {
228
+ uploadResponse(err);
229
+ return;
230
+ }
231
+ }
232
+ params.contentType ||= ContentType[i];
110
233
  if (metadata) {
111
234
  params.customMetadata = metadata;
112
235
  }
@@ -122,7 +245,7 @@ module.exports = function upload(credential, service = 'gcp') {
122
245
  uploadResponse(null, url);
123
246
  }
124
247
  })
125
- .catch(err => {
248
+ .catch((err) => {
126
249
  if (first) {
127
250
  uploadResponse(err);
128
251
  }
@@ -136,14 +259,14 @@ module.exports = function upload(credential, service = 'gcp') {
136
259
  const readable = publicRead || active && publicRead !== false && publicRead !== 0 && !acl;
137
260
  let ACL, revoke, predefined;
138
261
  if (first) {
139
- params.contentType || (params.contentType = ContentType[i]);
262
+ params.contentType ||= ContentType[i];
140
263
  if (readable) {
141
- ACL = 'publicRead';
264
+ ACL = "publicRead";
142
265
  }
143
266
  else {
144
267
  predefined = true;
145
268
  }
146
- if (typeof metadata === 'object') {
269
+ if ((0, types_1.isObject)(metadata)) {
147
270
  params.metadata = metadata;
148
271
  }
149
272
  }
@@ -151,7 +274,7 @@ module.exports = function upload(credential, service = 'gcp') {
151
274
  params.contentType = ContentType[i];
152
275
  if (!params.predefinedAcl) {
153
276
  if (readable) {
154
- ACL = 'publicRead';
277
+ ACL = "publicRead";
155
278
  }
156
279
  else {
157
280
  predefined = true;
@@ -160,26 +283,47 @@ module.exports = function upload(credential, service = 'gcp') {
160
283
  }
161
284
  if (predefined) {
162
285
  if (publicRead === 0) {
163
- ACL = 'publicRead';
286
+ ACL = "publicRead";
164
287
  revoke = true;
165
288
  }
166
289
  if (acl && (!revoke || acl !== ACL)) {
167
290
  params.predefinedAcl = acl;
168
291
  }
169
292
  }
170
- params.resumable ?? (params.resumable = false);
171
293
  const file = bucketClient.file(destination);
172
- file.save(Body[i], params).then(async () => {
294
+ let uploadClient;
295
+ if (Path[i]) {
296
+ const { TransferManager } = require('@google-cloud/storage');
297
+ uploadClient = new TransferManager(bucketClient).uploadFileInChunks(Path[i], { uploadName: destination, chunkSizeBytes, headers, concurrencyLimit: data.upload.chunkLimit || concurrencyLimit, maxQueueSize });
298
+ }
299
+ else {
300
+ params.resumable ??= false;
301
+ if (Stream[i]) {
302
+ if (chunkSizeBytes > 0) {
303
+ params.chunkSize = chunkSizeBytes;
304
+ }
305
+ uploadClient = new Promise((resolve, reject) => {
306
+ Stream[i].pipe(file.createWriteStream(params))
307
+ .on('finish', resolve)
308
+ .on('error', reject);
309
+ });
310
+ }
311
+ }
312
+ (uploadClient || file.save(Body[i], params))
313
+ .then(async () => {
173
314
  const url = getURL(file.name);
174
315
  this.formatMessage(64, service, "Upload success", url, { ...Cloud.LOG_CLOUD_UPLOAD });
175
316
  if (first) {
176
317
  uploadResponse(null, url);
177
318
  }
319
+ if (Path[i]) {
320
+ file.setMetadata(params).catch((err) => addLog(err));
321
+ }
178
322
  if (ACL) {
179
323
  client_1.setPredefinedAcl.call(this, file.acl, ACL, file.bucket, file.name, revoke || (await file.getMetadata())[0], true);
180
324
  }
181
325
  })
182
- .catch(err => {
326
+ .catch((err) => {
183
327
  if (first) {
184
328
  uploadResponse(err);
185
329
  }