@pi-r/gcp 0.6.5 → 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,19 +1,16 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- 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;
4
3
  const path = require("path");
5
4
  const storage = require("@google-cloud/storage");
6
5
  const firestore_1 = require("@google-cloud/firestore");
7
6
  const util_1 = require("@e-mc/cloud/util");
8
7
  const types_1 = require("@e-mc/types");
9
- const Module = require("@e-mc/module");
10
8
  const Cloud = require("@e-mc/cloud");
11
9
  const FIREBASE_DB = {};
12
10
  const FIREBASE_STORAGE = {};
13
11
  const ADMIN_DB = {};
14
12
  const ADMIN_STORAGE = {};
15
13
  function getApp(credential, isDB) {
16
- var _a, _b;
17
14
  const { projectId, databaseURL, storageBucket, apiKey, authDomain } = credential;
18
15
  if (apiKey && authDomain) {
19
16
  const { getApps, initializeApp } = require('@firebase/app');
@@ -27,7 +24,7 @@ function getApp(credential, isDB) {
27
24
  }
28
25
  if (!app) {
29
26
  const firebaseKey = (projectId || '') + '_' + apiKey + '_' + authDomain + (isDB ? '_' + databaseURL : '');
30
- 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);
31
28
  }
32
29
  if (isDB) {
33
30
  const { getDatabase, goOnline } = require('@firebase/database');
@@ -48,7 +45,7 @@ function getApp(credential, isDB) {
48
45
  }
49
46
  }
50
47
  if (!app) {
51
- 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);
52
49
  }
53
50
  credential.admin = true;
54
51
  if (isDB) {
@@ -60,7 +57,7 @@ function getApp(credential, isDB) {
60
57
  }
61
58
  }
62
59
  async function hasBucket(client) {
63
- const [exists] = await client.exists().catch(err => isErrorConflict(err) ? [true] : [false]);
60
+ const [exists] = await client.exists().catch((err) => isErrorConflict(err) ? [true] : [false]);
64
61
  return exists;
65
62
  }
66
63
  async function updatePredefinedAcl(acl, values, metadata = {}) {
@@ -84,7 +81,7 @@ async function updatePredefinedAcl(acl, values, metadata = {}) {
84
81
  }
85
82
  if ((0, types_1.isString)(value.role)) {
86
83
  if (projectNumber !== undefined) {
87
- value.userProject || (value.userProject = projectNumber.toString());
84
+ value.userProject ||= projectNumber.toString();
88
85
  }
89
86
  return target.add(value);
90
87
  }
@@ -100,24 +97,24 @@ async function updatePredefinedAcl(acl, values, metadata = {}) {
100
97
  }
101
98
  return errors;
102
99
  })
103
- .catch(err => [err]);
100
+ .catch((err) => [err]);
104
101
  }
105
102
  async function setUniformBucketLevel(bucket, enabled) {
106
103
  return bucket.setMetadata({ iamConfiguration: { uniformBucketLevelAccess: { enabled } } });
107
104
  }
108
105
  const getFirebasePackageName = (credential) => credential.apiKey && credential.authDomain ? 'firebase' : 'firebase-admin';
109
- const hasKeyFile = (credential) => (0, types_1.isString)(credential.keyFilename || credential.keyFile || process.env.GOOGLE_APPLICATION_CREDENTIALS) || (0, types_1.isObject)(credential.credentials);
110
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;
111
108
  function isFirebaseApp(credential, store, admin) {
112
- 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);
113
110
  }
114
111
  exports.isFirebaseApp = isFirebaseApp;
115
112
  function validateStorage(credential) {
116
- return hasKeyFile(credential) || !!credential.authClient || isFirebaseApp(credential, true);
113
+ return hasGoogleAuth(credential) || isFirebaseApp(credential, true);
117
114
  }
118
115
  exports.validateStorage = validateStorage;
119
116
  function validateDatabase(credential, data) {
120
- 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);
121
118
  }
122
119
  exports.validateDatabase = validateDatabase;
123
120
  function createStorageClient(credential, bucket) {
@@ -142,29 +139,26 @@ exports.createStorageClient = createStorageClient;
142
139
  function createDatabaseClient(credential, data) {
143
140
  let packageName = '';
144
141
  try {
145
- let product, name, database, table, query;
146
- if (data) {
147
- ({ product, name, table, query, database } = data);
148
- }
149
- if (product === 'firebase' || isFirebaseApp(credential)) {
142
+ if (data?.product === "firebase" || isFirebaseApp(credential)) {
150
143
  packageName = getFirebasePackageName(credential);
151
144
  return getApp(credential, true);
152
145
  }
153
146
  getProjectId(credential, true);
154
147
  if (data) {
155
- 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)) {
156
150
  const { Spanner } = require(packageName = '@google-cloud/spanner');
157
151
  return new Spanner(credential);
158
152
  }
159
- 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))) {
160
154
  const { BigQuery } = require(packageName = '@google-cloud/bigquery');
161
155
  return new BigQuery(credential);
162
156
  }
163
- if (product === 'bigtable' || !product && name && table && !database) {
157
+ if (product === "bigtable" || !product && name && table && !database) {
164
158
  const { Bigtable } = require(packageName = '@google-cloud/bigtable');
165
159
  return new Bigtable(credential);
166
160
  }
167
- 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)) {
168
162
  const { Datastore } = require(packageName = '@google-cloud/datastore');
169
163
  return new Datastore(credential);
170
164
  }
@@ -223,32 +217,32 @@ async function setBucketPolicy(credential, bucket, options, service = 'gcp') {
223
217
  if (!Array.isArray(options.acl)) {
224
218
  options.acl = undefined;
225
219
  switch (acl) {
226
- case 'private':
227
- case 'projectPrivate':
228
- await client.makePrivate(options).catch(err => !isErrorConflict(err) && errors.push(err));
229
- 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));
230
224
  break;
231
- case 'authenticatedRead':
232
- 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));
233
227
  options = { ...options };
234
228
  options.acl = [{ entity: 'allUsers' }, { entity: 'allAuthenticatedUsers', role: 'READER' }];
235
229
  break;
236
- case 'publicReadWrite':
237
- 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));
238
232
  options = { ...options };
239
233
  options.acl = [{ entity: 'allUsers', role: 'WRITER' }];
240
234
  break;
241
- case 'publicRead':
242
- 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));
243
237
  break;
244
238
  case 'bucketAccessUniform':
245
- await setUniformBucketLevel(client, true).catch(err => errors.push(err));
239
+ await setUniformBucketLevel(client, true).catch((err) => errors.push(err));
246
240
  break;
247
241
  case 'bucketAccessACL':
248
- await setUniformBucketLevel(client, false).catch(err => errors.push(err));
242
+ await setUniformBucketLevel(client, false).catch((err) => errors.push(err));
249
243
  break;
250
244
  default:
251
- 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'));
252
246
  break;
253
247
  }
254
248
  }
@@ -263,6 +257,33 @@ async function setBucketPolicy(credential, bucket, options, service = 'gcp') {
263
257
  return true;
264
258
  }
265
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;
266
287
  async function setBucketWebsite(credential, bucket, options, service = 'gcp') {
267
288
  if (isFirebaseApp(credential, true, true)) {
268
289
  return false;
@@ -277,11 +298,11 @@ async function setBucketWebsite(credential, bucket, options, service = 'gcp') {
277
298
  }
278
299
  return createStorageClient.call(this, credential, bucket).bucket(bucket).setMetadata({ website })
279
300
  .then(() => {
280
- 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 });
281
302
  return true;
282
303
  })
283
- .catch(err => {
284
- 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 });
285
306
  return false;
286
307
  });
287
308
  }
@@ -305,7 +326,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
305
326
  }
306
327
  if (recursive && prefixes.length) {
307
328
  for (const { name } of prefixes) {
308
- await recurse(Module.joinPath(prefix, name));
329
+ await recurse(Cloud.joinPath(prefix, name));
309
330
  }
310
331
  }
311
332
  }
@@ -328,7 +349,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
328
349
  const result = await client.getFiles();
329
350
  for (const { name } of result[0]) {
330
351
  if (!name.includes('/')) {
331
- 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 })));
332
353
  }
333
354
  }
334
355
  }
@@ -355,7 +376,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
355
376
  this.formatMessage(64, service, ["Bucket emptied" + ` (${recursive ? 'recursive' : files})`, bucket], recursive ? files : '', { ...Cloud.LOG_CLOUD_COMMAND });
356
377
  }
357
378
  })
358
- .catch(err => {
379
+ .catch((err) => {
359
380
  errors.push(err);
360
381
  });
361
382
  }
@@ -369,7 +390,7 @@ exports.executeQuery = executeQuery;
369
390
  async function executeBatchQuery(credential, batch, sessionKey) {
370
391
  const length = batch.length;
371
392
  const result = new Array(length);
372
- const clients = [];
393
+ const clients = {};
373
394
  const caching = length > 0 && this.hasCache(batch[0].service, sessionKey);
374
395
  const cacheValue = { value: this.valueOfKey(credential, 'cache'), sessionKey };
375
396
  const authItems = [];
@@ -384,7 +405,14 @@ async function executeBatchQuery(credential, batch, sessionKey) {
384
405
  };
385
406
  const closeClient = () => {
386
407
  const fs = clients[3];
387
- fs?.terminate();
408
+ if (fs) {
409
+ fs.terminate();
410
+ return;
411
+ }
412
+ const sp = clients[5];
413
+ if (sp) {
414
+ sp.close();
415
+ }
388
416
  };
389
417
  const getFamily = (data, row, family) => {
390
418
  const familyData = data[family];
@@ -417,14 +445,14 @@ async function executeBatchQuery(credential, batch, sessionKey) {
417
445
  };
418
446
  cacheValue.exclusiveOf = Array.isArray(ignoreCache) ? ignoreCache : undefined;
419
447
  let rows, queryString = '';
420
- if (product === 'firebase' || isFirebaseApp(credential)) {
448
+ if (product === "firebase" || isFirebaseApp(credential)) {
421
449
  if (!(0, types_1.isString)(query)) {
422
450
  closeClient();
423
451
  throw (0, util_1.formatError)(item, "Missing database query");
424
452
  }
425
453
  const orderBy = item.orderBy;
426
454
  if (useCache) {
427
- queryString = query + Module.asString(orderBy, true);
455
+ queryString = query + Cloud.asString(orderBy, true);
428
456
  if (!update && (rows = getCache(queryString))) {
429
457
  result[i] = rows;
430
458
  continue;
@@ -473,7 +501,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
473
501
  rows = [group && (0, types_1.isObject)(val) && (0, types_1.isObject)(val[group[0]]) ? val[group[0]] : val];
474
502
  }
475
503
  }
476
- else if (product === 'spanner' || !product && name && database && (query || table)) {
504
+ else if (product === "spanner" || !product && name && database && (query || table)) {
477
505
  const columns = (0, types_1.isPlainObject)(query) && query.columns || item.columns;
478
506
  if (!(query || table && columns)) {
479
507
  closeClient();
@@ -485,7 +513,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
485
513
  }
486
514
  const { updateType, options = {} } = item;
487
515
  if (useCache) {
488
- 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 : '');
489
517
  if (!update && (rows = getCache(queryString))) {
490
518
  result[i] = rows;
491
519
  continue;
@@ -516,8 +544,12 @@ async function executeBatchQuery(credential, batch, sessionKey) {
516
544
  }
517
545
  else {
518
546
  transaction.runUpdate(update)
519
- .then(async () => transaction.commit().then(() => resolve()).catch(error => reject(error)))
520
- .catch(error => reject(error));
547
+ .then(async () => {
548
+ transaction.commit()
549
+ .then(() => resolve())
550
+ .catch(reject);
551
+ })
552
+ .catch(reject);
521
553
  }
522
554
  });
523
555
  });
@@ -525,12 +557,12 @@ async function executeBatchQuery(credential, batch, sessionKey) {
525
557
  const request = (0, types_1.isPlainObject)(query) ? query : { sql: query };
526
558
  request.json = true;
527
559
  if (limit > 0) {
528
- (request.gaxOptions || (request.gaxOptions = {})).maxResults = limit;
560
+ (request.gaxOptions ||= {}).maxResults = limit;
529
561
  }
530
562
  [rows] = await db.run(request);
531
563
  }
532
564
  }
533
- else if (product === 'bigtable' || !product && name && table && !database) {
565
+ else if (product === "bigtable" || !product && name && table && !database) {
534
566
  if (!name || !table) {
535
567
  closeClient();
536
568
  throw (0, util_1.formatError)(item, !name ? "Missing database name" : "Missing database table");
@@ -540,10 +572,10 @@ async function executeBatchQuery(credential, batch, sessionKey) {
540
572
  options.filter = query;
541
573
  }
542
574
  if (!id && limit > 0) {
543
- (options.gaxOptions || (options.gaxOptions = {})).maxResults = limit;
575
+ (options.gaxOptions ||= {}).maxResults = limit;
544
576
  }
545
577
  if (useCache) {
546
- 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);
547
579
  if (!(update && id) && (rows = getCache(queryString))) {
548
580
  result[i] = rows;
549
581
  continue;
@@ -574,7 +606,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
574
606
  });
575
607
  }
576
608
  }
577
- 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))) {
578
610
  const { params, options = {} } = item;
579
611
  if ((0, types_1.isString)(query)) {
580
612
  options.query = query;
@@ -586,7 +618,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
586
618
  options.maxResults = limit;
587
619
  }
588
620
  if (useCache) {
589
- queryString = query + (database ? '_' + database + (table ? '_' + table : '') : '') + Module.asString(options, true);
621
+ queryString = query + (database ? '_' + database + (table ? '_' + table : '') : '') + Cloud.asString(options, true);
590
622
  if (!update && (rows = getCache(queryString))) {
591
623
  result[i] = rows;
592
624
  continue;
@@ -613,7 +645,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
613
645
  }
614
646
  [rows] = await job.getQueryResults();
615
647
  }
616
- 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)) {
617
649
  const { keys, kind } = item;
618
650
  if (!keys && !kind && !Array.isArray(query)) {
619
651
  closeClient();
@@ -669,19 +701,19 @@ async function executeBatchQuery(credential, batch, sessionKey) {
669
701
  db.runQuery(task.select('__key__'), item.options).then(entities => {
670
702
  if ((0, types_1.isArray)(entities)) {
671
703
  const items = entities.map((entity) => db.key(entity[db.KEY]));
672
- 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));
673
705
  }
674
706
  else {
675
707
  resolve();
676
708
  }
677
709
  })
678
- .catch(err => reject(err));
710
+ .catch((err) => reject(err));
679
711
  });
680
712
  }
681
713
  [rows] = await db.runQuery(task, item.options);
682
714
  }
683
715
  }
684
- else if (product === 'firestore' || !product && table) {
716
+ else if (product === "firestore" || !product && table) {
685
717
  if (!table) {
686
718
  closeClient();
687
719
  throw (0, util_1.formatError)(item, "Missing database table");
@@ -705,7 +737,34 @@ async function executeBatchQuery(credential, batch, sessionKey) {
705
737
  else {
706
738
  const doc = db.collection(table).doc(id);
707
739
  if ((0, types_1.isPlainObject)(update)) {
708
- 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
+ }
709
768
  }
710
769
  const data = (await doc.get()).data();
711
770
  if (data) {
@@ -715,7 +774,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
715
774
  }
716
775
  else if (Array.isArray(query)) {
717
776
  const orderBy = item.orderBy;
718
- 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 : '')))) {
719
778
  result[i] = rows;
720
779
  continue;
721
780
  }
@@ -727,7 +786,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
727
786
  switch (method) {
728
787
  case 'whereAnd':
729
788
  case 'whereOr':
730
- if (params.every(p => Array.isArray(p))) {
789
+ if (params.every(p => (0, types_1.isArray)(p))) {
731
790
  db = db.where(firestore_1.Filter[method === 'whereAnd' ? 'and' : 'or'](...params.map((p) => firestore_1.Filter.where(p[0], p[1], p[2]))));
732
791
  }
733
792
  else {
@@ -784,7 +843,7 @@ function setPublicRead(acl, pathname, requested, service = 'gcp') {
784
843
  this.formatMessage(64, service, "Grant public-read", pathname, { ...Cloud.LOG_CLOUD_COMMAND });
785
844
  }
786
845
  })
787
- .catch(err => {
846
+ .catch((err) => {
788
847
  if (requested) {
789
848
  this.formatMessage(64, service, ["Unable to grant public-read", pathname], err, { ...Cloud.LOG_CLOUD_WARN });
790
849
  }
@@ -816,7 +875,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
816
875
  items = [{ entity: 'allUsers' }, { entity: 'allAuthenticatedUsers' }];
817
876
  }
818
877
  switch (value) {
819
- case 'private': {
878
+ case "private": {
820
879
  const owners = new Set(acl.filter(item => item.role === 'OWNER' && item.entity).map(item => item.entity));
821
880
  if (owner) {
822
881
  owners.add(owner);
@@ -831,7 +890,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
831
890
  }
832
891
  break;
833
892
  }
834
- case 'projectPrivate':
893
+ case "projectPrivate":
835
894
  for (const user of acl) {
836
895
  const entity = user.entity;
837
896
  if (entity) {
@@ -840,7 +899,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
840
899
  }
841
900
  }
842
901
  break;
843
- case 'bucketOwnerRead':
902
+ case "bucketOwnerRead":
844
903
  if (bucket && owner) {
845
904
  for (const user of acl) {
846
905
  const entity = user.entity;
@@ -849,7 +908,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
849
908
  }
850
909
  }
851
910
  }
852
- case 'bucketOwnerFullControl':
911
+ case "bucketOwnerFullControl":
853
912
  if (bucket && owner) {
854
913
  items.push({ entity: owner, role: 'OWNER' });
855
914
  }
@@ -916,13 +975,16 @@ function getProjectId(credential, cache) {
916
975
  const id = require(pathname).project_id;
917
976
  if ((0, types_1.isString)(id)) {
918
977
  projectId = id;
919
- credential.projectId = projectId;
920
978
  }
921
979
  }
922
980
  catch {
923
981
  }
982
+ credential.projectId = projectId || (projectId = process.env.GCLOUD_PROJECT);
924
983
  }
925
984
  }
926
985
  return projectId;
927
986
  }
928
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
- function download(credential, service = 'gcp') {
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 @@ 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 @@ 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,28 +52,36 @@ 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
- }
80
-
81
- module.exports = download;
87
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-r/gcp",
3
- "version": "0.6.5",
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.6",
24
- "@e-mc/module": "^0.8.6",
25
- "@e-mc/types": "^0.8.6",
26
- "@google-cloud/firestore": "^7.3.1",
27
- "@google-cloud/storage": "^7.7.0"
23
+ "@e-mc/cloud": "^0.9.0",
24
+ "@e-mc/module": "^0.9.0",
25
+ "@e-mc/types": "^0.9.0",
26
+ "@google-cloud/firestore": "^7.6.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
- function upload(credential, service = 'gcp') {
11
+ const getBucketKey = (credential, bucket, acl = '') => Cloud.asString(credential, true) + bucket + '_' + acl;
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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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
  }
@@ -190,6 +334,4 @@ function upload(credential, service = 'gcp') {
190
334
  }
191
335
  }
192
336
  };
193
- }
194
-
195
- module.exports = upload;
337
+ };