firebase-tools 10.1.5 → 10.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.js +1 -0
- package/lib/apiv2.js +3 -0
- package/lib/appdistribution/options-parser-util.js +1 -1
- package/lib/auth.js +62 -25
- package/lib/command.js +1 -1
- package/lib/commands/apps-android-sha-create.js +2 -2
- package/lib/commands/apps-sdkconfig.js +1 -1
- package/lib/commands/auth-import.js +1 -1
- package/lib/commands/database-rules-list.js +2 -2
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/ext-configure.js +1 -0
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +12 -2
- package/lib/commands/ext-install.js +104 -103
- package/lib/commands/ext-uninstall.js +9 -8
- package/lib/commands/ext-update.js +10 -9
- package/lib/commands/functions-config-clone.js +1 -1
- package/lib/commands/functions-config-export.js +1 -1
- package/lib/commands/functions-secrets-access.js +17 -0
- package/lib/commands/functions-secrets-destroy.js +40 -0
- package/lib/commands/functions-secrets-get.js +21 -0
- package/lib/commands/functions-secrets-prune.js +50 -0
- package/lib/commands/functions-secrets-set.js +46 -0
- package/lib/commands/hosting-clone.js +3 -3
- package/lib/commands/index.js +7 -3
- package/lib/commands/login.js +1 -1
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/deploy/extensions/deploymentSummary.js +3 -3
- package/lib/deploy/extensions/params.js +3 -0
- package/lib/deploy/extensions/planner.js +2 -1
- package/lib/deploy/extensions/tasks.js +1 -1
- package/lib/deploy/functions/backend.js +20 -5
- package/lib/deploy/functions/checkIam.js +1 -1
- package/lib/deploy/functions/containerCleaner.js +3 -3
- package/lib/deploy/functions/ensure.js +112 -0
- package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
- package/lib/deploy/functions/functionsDeployHelper.js +2 -2
- package/lib/deploy/functions/prepare.js +15 -20
- package/lib/deploy/functions/pricing.js +1 -1
- package/lib/deploy/functions/prompts.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +3 -3
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/planner.js +11 -8
- package/lib/deploy/functions/release/reporter.js +3 -0
- package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
- package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +17 -11
- package/lib/deploy/functions/runtimes/golang/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/index.js +26 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +40 -7
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/validate.js +58 -3
- package/lib/deploy/hosting/client.js +9 -0
- package/lib/deploy/hosting/convertConfig.js +6 -0
- package/lib/deploy/hosting/deploy.js +2 -2
- package/lib/deploy/hosting/hashcache.js +21 -19
- package/lib/deploy/hosting/index.js +5 -5
- package/lib/deploy/hosting/prepare.js +25 -25
- package/lib/deploy/hosting/release.js +21 -24
- package/lib/deploy/hosting/uploader.js +5 -5
- package/lib/deploy/remoteconfig/functions.js +2 -2
- package/lib/emulator/auth/cloudFunctions.js +1 -1
- package/lib/emulator/auth/operations.js +1 -1
- package/lib/emulator/commandUtils.js +5 -1
- package/lib/emulator/constants.js +3 -0
- package/lib/emulator/controller.js +48 -18
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +30 -13
- package/lib/emulator/emulatorLogger.js +19 -1
- package/lib/emulator/extensions/validation.js +35 -0
- package/lib/emulator/extensionsEmulator.js +140 -0
- package/lib/emulator/functionsEmulator.js +175 -86
- package/lib/emulator/functionsEmulatorRuntime.js +108 -83
- package/lib/emulator/functionsEmulatorShared.js +51 -1
- package/lib/emulator/functionsEmulatorShell.js +1 -2
- package/lib/emulator/functionsEmulatorUtils.js +4 -4
- package/lib/emulator/functionsRuntimeWorker.js +3 -3
- package/lib/emulator/hub.js +4 -3
- package/lib/emulator/loggingEmulator.js +1 -1
- package/lib/emulator/pubsubEmulator.js +1 -1
- package/lib/emulator/registry.js +10 -2
- package/lib/emulator/storage/apis/firebase.js +31 -26
- package/lib/emulator/storage/apis/gcloud.js +7 -12
- package/lib/emulator/storage/files.js +36 -34
- package/lib/emulator/storage/index.js +2 -2
- package/lib/emulator/storage/metadata.js +2 -2
- package/lib/emulator/storage/rules/runtime.js +8 -7
- package/lib/emulator/types.js +3 -0
- package/lib/ensureApiEnabled.js +5 -1
- package/lib/error.js +1 -1
- package/lib/extensions/askUserForParam.js +2 -2
- package/lib/extensions/changelog.js +3 -1
- package/lib/extensions/checkProjectBilling.js +1 -1
- package/lib/extensions/diagnose.js +56 -0
- package/lib/extensions/displayExtensionInfo.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +24 -8
- package/lib/extensions/emulator/specHelper.js +10 -23
- package/lib/extensions/export.js +1 -51
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +23 -10
- package/lib/extensions/listExtensions.js +2 -0
- package/lib/extensions/manifest.js +48 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +4 -4
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/secretsUtils.js +4 -4
- package/lib/functional.js +1 -1
- package/lib/functions/env.js +7 -8
- package/lib/functions/secrets.js +112 -0
- package/lib/gcp/cloudfunctions.js +24 -5
- package/lib/gcp/cloudfunctionsv2.js +18 -5
- package/lib/gcp/cloudtasks.js +1 -1
- package/lib/gcp/docker.js +2 -2
- package/lib/gcp/run.js +2 -2
- package/lib/gcp/secretManager.js +128 -46
- package/lib/gcp/storage.js +1 -0
- package/lib/hosting/api.js +1 -1
- package/lib/hosting/functionsProxy.js +15 -5
- package/lib/hosting/proxy.js +2 -2
- package/lib/init/features/account.js +1 -1
- package/lib/management/database.js +1 -1
- package/lib/previews.js +1 -1
- package/lib/responseToError.js +16 -7
- package/lib/serve/functions.js +2 -2
- package/lib/serve/hosting.js +1 -1
- package/lib/utils.js +7 -2
- package/npm-shrinkwrap.json +904 -412
- package/package.json +3 -3
- package/schema/firebase-config.json +32 -0
- package/templates/init/functions/javascript/package.lint.json +3 -3
- package/templates/init/functions/javascript/package.nolint.json +2 -2
- package/templates/init/functions/typescript/package.lint.json +7 -7
- package/templates/init/functions/typescript/package.nolint.json +3 -3
|
@@ -114,10 +114,10 @@ function createFirebaseEndpoints(emulator) {
|
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
let isGZipped = false;
|
|
117
|
-
if (md.contentEncoding
|
|
117
|
+
if (md.contentEncoding === "gzip") {
|
|
118
118
|
isGZipped = true;
|
|
119
119
|
}
|
|
120
|
-
if (req.query.alt
|
|
120
|
+
if (req.query.alt === "media") {
|
|
121
121
|
let data = storageLayer.getBytes(req.params.bucketId, req.params.objectId);
|
|
122
122
|
if (!data) {
|
|
123
123
|
res.sendStatus(404);
|
|
@@ -206,7 +206,6 @@ function createFirebaseEndpoints(emulator) {
|
|
|
206
206
|
res.json(storageLayer.listItemsAndPrefixes(req.params.bucketId, prefix, delimiter, pageToken, maxRes));
|
|
207
207
|
});
|
|
208
208
|
const handleUpload = async (req, res) => {
|
|
209
|
-
var _a;
|
|
210
209
|
if (req.query.create_token || req.query.delete_token) {
|
|
211
210
|
const decodedObjectId = decodeURIComponent(req.params.objectId);
|
|
212
211
|
const operationPath = ["b", req.params.bucketId, "o", decodedObjectId].join("/");
|
|
@@ -239,7 +238,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
239
238
|
const deleteTokenParam = req.query["delete_token"];
|
|
240
239
|
let md;
|
|
241
240
|
if (createTokenParam) {
|
|
242
|
-
if (createTokenParam
|
|
241
|
+
if (createTokenParam !== "true") {
|
|
243
242
|
res.sendStatus(400);
|
|
244
243
|
return;
|
|
245
244
|
}
|
|
@@ -261,7 +260,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
261
260
|
}
|
|
262
261
|
const name = req.query.name.toString();
|
|
263
262
|
const uploadType = req.header("x-goog-upload-protocol");
|
|
264
|
-
if (uploadType
|
|
263
|
+
if (uploadType === "multipart") {
|
|
265
264
|
const contentType = req.header("content-type");
|
|
266
265
|
if (!contentType || !contentType.startsWith("multipart/related")) {
|
|
267
266
|
res.sendStatus(400);
|
|
@@ -311,7 +310,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
311
310
|
},
|
|
312
311
|
});
|
|
313
312
|
}
|
|
314
|
-
if (md.downloadTokens.length
|
|
313
|
+
if (md.downloadTokens.length === 0) {
|
|
315
314
|
md.addDownloadToken();
|
|
316
315
|
}
|
|
317
316
|
res.json(new metadata_1.OutgoingFirebaseMetadata(md));
|
|
@@ -324,7 +323,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
324
323
|
res.sendStatus(400);
|
|
325
324
|
return;
|
|
326
325
|
}
|
|
327
|
-
if (uploadCommand
|
|
326
|
+
if (uploadCommand === "start") {
|
|
328
327
|
let objectContentType = req.header("x-goog-upload-header-content-type") ||
|
|
329
328
|
req.header("x-goog-upload-content-type");
|
|
330
329
|
if (!objectContentType) {
|
|
@@ -336,7 +335,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
336
335
|
objectContentType = mimeTypeFromName;
|
|
337
336
|
}
|
|
338
337
|
}
|
|
339
|
-
const upload = storageLayer.startUpload(req.params.bucketId, name, objectContentType, req.body);
|
|
338
|
+
const upload = storageLayer.startUpload(req.params.bucketId, name, objectContentType, req.body, req.header("authorization"));
|
|
340
339
|
storageLayer.uploadBytes(upload.uploadId, Buffer.alloc(0));
|
|
341
340
|
const emulatorInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE);
|
|
342
341
|
res.header("x-goog-upload-chunk-granularity", "10000");
|
|
@@ -352,7 +351,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
352
351
|
return;
|
|
353
352
|
}
|
|
354
353
|
const uploadId = req.query.upload_id.toString();
|
|
355
|
-
if (uploadCommand
|
|
354
|
+
if (uploadCommand === "query") {
|
|
356
355
|
const upload = storageLayer.queryUpload(uploadId);
|
|
357
356
|
if (!upload) {
|
|
358
357
|
res.sendStatus(400);
|
|
@@ -362,13 +361,15 @@ function createFirebaseEndpoints(emulator) {
|
|
|
362
361
|
res.sendStatus(200);
|
|
363
362
|
return;
|
|
364
363
|
}
|
|
365
|
-
if (uploadCommand
|
|
366
|
-
const upload = storageLayer.
|
|
367
|
-
if (
|
|
368
|
-
|
|
369
|
-
|
|
364
|
+
if (uploadCommand === "cancel") {
|
|
365
|
+
const upload = storageLayer.queryUpload(uploadId);
|
|
366
|
+
if (upload) {
|
|
367
|
+
const cancelled = storageLayer.cancelUpload(upload);
|
|
368
|
+
res.sendStatus(cancelled ? 200 : 400);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
res.sendStatus(404);
|
|
370
372
|
}
|
|
371
|
-
res.sendStatus(200);
|
|
372
373
|
return;
|
|
373
374
|
}
|
|
374
375
|
let upload;
|
|
@@ -394,20 +395,18 @@ function createFirebaseEndpoints(emulator) {
|
|
|
394
395
|
res.header("x-gupload-uploadid", upload.uploadId);
|
|
395
396
|
}
|
|
396
397
|
if (uploadCommand.includes("finalize")) {
|
|
397
|
-
|
|
398
|
-
if (!
|
|
398
|
+
upload = storageLayer.queryUpload(uploadId);
|
|
399
|
+
if (!upload) {
|
|
399
400
|
res.sendStatus(400);
|
|
400
401
|
return;
|
|
401
402
|
}
|
|
402
|
-
upload = finalizedUpload.upload;
|
|
403
|
-
res.header("x-goog-upload-status", "final");
|
|
404
403
|
if (!(await isPermitted({
|
|
405
404
|
ruleset: emulator.rules,
|
|
406
405
|
method: types_2.RulesetOperationMethod.CREATE,
|
|
407
406
|
path: operationPath,
|
|
408
|
-
authorization:
|
|
407
|
+
authorization: upload.authorization,
|
|
409
408
|
file: {
|
|
410
|
-
after:
|
|
409
|
+
after: storageLayer.createMetadata(upload).asRulesResource(),
|
|
411
410
|
},
|
|
412
411
|
}))) {
|
|
413
412
|
storageLayer.deleteFile(upload.bucketId, name);
|
|
@@ -418,11 +417,13 @@ function createFirebaseEndpoints(emulator) {
|
|
|
418
417
|
},
|
|
419
418
|
});
|
|
420
419
|
}
|
|
421
|
-
|
|
422
|
-
|
|
420
|
+
res.header("x-goog-upload-status", "final");
|
|
421
|
+
const uploadedFile = storageLayer.finalizeUpload(upload);
|
|
422
|
+
const md = uploadedFile.metadata;
|
|
423
|
+
if (md.downloadTokens.length === 0) {
|
|
423
424
|
md.addDownloadToken();
|
|
424
425
|
}
|
|
425
|
-
res.json(new metadata_1.OutgoingFirebaseMetadata(
|
|
426
|
+
res.json(new metadata_1.OutgoingFirebaseMetadata(uploadedFile.metadata));
|
|
426
427
|
}
|
|
427
428
|
else if (!upload) {
|
|
428
429
|
res.sendStatus(400);
|
|
@@ -447,12 +448,17 @@ function createFirebaseEndpoints(emulator) {
|
|
|
447
448
|
firebaseStorageAPI.delete("/b/:bucketId/o/:objectId", async (req, res) => {
|
|
448
449
|
const decodedObjectId = decodeURIComponent(req.params.objectId);
|
|
449
450
|
const operationPath = ["b", req.params.bucketId, "o", decodedObjectId].join("/");
|
|
451
|
+
const md = storageLayer.getMetadata(req.params.bucketId, decodedObjectId);
|
|
452
|
+
const rulesFiles = {};
|
|
453
|
+
if (md) {
|
|
454
|
+
rulesFiles.before = md.asRulesResource();
|
|
455
|
+
}
|
|
450
456
|
if (!(await isPermitted({
|
|
451
457
|
ruleset: emulator.rules,
|
|
452
458
|
method: types_2.RulesetOperationMethod.DELETE,
|
|
453
459
|
path: operationPath,
|
|
454
460
|
authorization: req.header("authorization"),
|
|
455
|
-
file:
|
|
461
|
+
file: rulesFiles,
|
|
456
462
|
}))) {
|
|
457
463
|
return res.status(403).json({
|
|
458
464
|
error: {
|
|
@@ -461,7 +467,6 @@ function createFirebaseEndpoints(emulator) {
|
|
|
461
467
|
},
|
|
462
468
|
});
|
|
463
469
|
}
|
|
464
|
-
const md = storageLayer.getMetadata(req.params.bucketId, decodedObjectId);
|
|
465
470
|
if (!md) {
|
|
466
471
|
res.sendStatus(404);
|
|
467
472
|
return;
|
|
@@ -26,7 +26,7 @@ function createCloudEndpoints(emulator) {
|
|
|
26
26
|
res.sendStatus(404);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
if (req.query.alt
|
|
29
|
+
if (req.query.alt === "media") {
|
|
30
30
|
return sendFileBytes(md, storageLayer, req, res);
|
|
31
31
|
}
|
|
32
32
|
const outgoingMd = new metadata_1.CloudStorageObjectMetadata(md);
|
|
@@ -80,18 +80,13 @@ function createCloudEndpoints(emulator) {
|
|
|
80
80
|
resolve();
|
|
81
81
|
});
|
|
82
82
|
});
|
|
83
|
-
|
|
83
|
+
const upload = storageLayer.uploadBytes(uploadId, req.body);
|
|
84
84
|
if (!upload) {
|
|
85
85
|
res.sendStatus(400);
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
res.sendStatus(400);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
upload = finalizedUpload.upload;
|
|
94
|
-
res.status(200).json(new metadata_1.CloudStorageObjectMetadata(finalizedUpload.file.metadata)).send();
|
|
88
|
+
const uploadedFile = storageLayer.finalizeUpload(upload);
|
|
89
|
+
res.status(200).json(new metadata_1.CloudStorageObjectMetadata(uploadedFile.metadata)).send();
|
|
95
90
|
});
|
|
96
91
|
gcloudStorageAPI.post("/b/:bucketId/o/:objectId/acl", (req, res) => {
|
|
97
92
|
var _a, _b;
|
|
@@ -130,10 +125,10 @@ function createCloudEndpoints(emulator) {
|
|
|
130
125
|
res.sendStatus(400);
|
|
131
126
|
return;
|
|
132
127
|
}
|
|
133
|
-
if (req.query.uploadType
|
|
128
|
+
if (req.query.uploadType === "resumable") {
|
|
134
129
|
const upload = storageLayer.startUpload(req.params.bucketId, name, contentType, req.body);
|
|
135
130
|
const emulatorInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE);
|
|
136
|
-
if (emulatorInfo
|
|
131
|
+
if (emulatorInfo === undefined) {
|
|
137
132
|
res.sendStatus(500);
|
|
138
133
|
return;
|
|
139
134
|
}
|
|
@@ -202,7 +197,7 @@ function sendFileBytes(md, storageLayer, req, res) {
|
|
|
202
197
|
res.sendStatus(404);
|
|
203
198
|
return;
|
|
204
199
|
}
|
|
205
|
-
const isGZipped = md.contentEncoding
|
|
200
|
+
const isGZipped = md.contentEncoding === "gzip";
|
|
206
201
|
if (isGZipped) {
|
|
207
202
|
data = (0, zlib_1.gunzipSync)(data);
|
|
208
203
|
}
|
|
@@ -40,7 +40,7 @@ class StoredFile {
|
|
|
40
40
|
}
|
|
41
41
|
exports.StoredFile = StoredFile;
|
|
42
42
|
class ResumableUpload {
|
|
43
|
-
constructor(bucketId, objectId, uploadId, contentType, metadata) {
|
|
43
|
+
constructor(bucketId, objectId, uploadId, contentType, metadata, authorization) {
|
|
44
44
|
this._currentBytesUploaded = 0;
|
|
45
45
|
this._status = UploadStatus.ACTIVE;
|
|
46
46
|
this._bucketId = bucketId;
|
|
@@ -48,6 +48,7 @@ class ResumableUpload {
|
|
|
48
48
|
this._uploadId = uploadId;
|
|
49
49
|
this._contentType = contentType;
|
|
50
50
|
this._metadata = metadata;
|
|
51
|
+
this._authorization = authorization;
|
|
51
52
|
this._fileLocation = encodeURIComponent(`${uploadId}_b_${bucketId}_o_${objectId}`);
|
|
52
53
|
this._currentBytesUploaded = 0;
|
|
53
54
|
}
|
|
@@ -69,6 +70,9 @@ class ResumableUpload {
|
|
|
69
70
|
set contentType(contentType) {
|
|
70
71
|
this._contentType = contentType;
|
|
71
72
|
}
|
|
73
|
+
get authorization() {
|
|
74
|
+
return this._authorization;
|
|
75
|
+
}
|
|
72
76
|
get currentBytesUploaded() {
|
|
73
77
|
return this._currentBytesUploaded;
|
|
74
78
|
}
|
|
@@ -110,7 +114,7 @@ class StorageLayer {
|
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
async listBuckets() {
|
|
113
|
-
if (this._buckets.size
|
|
117
|
+
if (this._buckets.size === 0) {
|
|
114
118
|
let adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this._projectId);
|
|
115
119
|
if (!adminSdkConfig) {
|
|
116
120
|
adminSdkConfig = (0, adminSdkConfig_1.constructDefaultAdminSdkConfig)(this._projectId);
|
|
@@ -127,6 +131,16 @@ class StorageLayer {
|
|
|
127
131
|
}
|
|
128
132
|
return;
|
|
129
133
|
}
|
|
134
|
+
createMetadata(upload) {
|
|
135
|
+
const bytes = this._persistence.readBytes(upload.fileLocation, upload.currentBytesUploaded);
|
|
136
|
+
return new metadata_1.StoredFileMetadata({
|
|
137
|
+
name: upload.objectId,
|
|
138
|
+
bucket: upload.bucketId,
|
|
139
|
+
contentType: "",
|
|
140
|
+
contentEncoding: upload.metadata.contentEncoding,
|
|
141
|
+
customMetadata: upload.metadata.metadata,
|
|
142
|
+
}, this._cloudFunctions, bytes);
|
|
143
|
+
}
|
|
130
144
|
getBytes(bucket, object, size, offset) {
|
|
131
145
|
const key = this.path(bucket, object);
|
|
132
146
|
const val = this._files.get(key);
|
|
@@ -139,22 +153,21 @@ class StorageLayer {
|
|
|
139
153
|
public(value) {
|
|
140
154
|
this._files = value;
|
|
141
155
|
}
|
|
142
|
-
startUpload(bucket, object, contentType, metadata) {
|
|
156
|
+
startUpload(bucket, object, contentType, metadata, authorization) {
|
|
143
157
|
const uploadId = (0, uuid_1.v4)();
|
|
144
|
-
const upload = new ResumableUpload(bucket, object, uploadId, contentType, metadata);
|
|
158
|
+
const upload = new ResumableUpload(bucket, object, uploadId, contentType, metadata, authorization);
|
|
145
159
|
this._uploads.set(uploadId, upload);
|
|
146
160
|
return upload;
|
|
147
161
|
}
|
|
148
162
|
queryUpload(uploadId) {
|
|
149
163
|
return this._uploads.get(uploadId);
|
|
150
164
|
}
|
|
151
|
-
cancelUpload(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
165
|
+
cancelUpload(upload) {
|
|
166
|
+
if (upload.status === UploadStatus.ACTIVE) {
|
|
167
|
+
this._persistence.deleteFile(upload.fileLocation);
|
|
168
|
+
upload.status = UploadStatus.CANCELLED;
|
|
155
169
|
}
|
|
156
|
-
upload.status
|
|
157
|
-
this._persistence.deleteFile(upload.fileLocation);
|
|
170
|
+
return upload.status === UploadStatus.CANCELLED;
|
|
158
171
|
}
|
|
159
172
|
uploadBytes(uploadId, bytes) {
|
|
160
173
|
const upload = this._uploads.get(uploadId);
|
|
@@ -175,7 +188,7 @@ class StorageLayer {
|
|
|
175
188
|
filePath += "%2F";
|
|
176
189
|
}
|
|
177
190
|
const file = this._files.get(filePath);
|
|
178
|
-
if (file
|
|
191
|
+
if (file === undefined) {
|
|
179
192
|
return false;
|
|
180
193
|
}
|
|
181
194
|
else {
|
|
@@ -188,27 +201,16 @@ class StorageLayer {
|
|
|
188
201
|
async deleteAll() {
|
|
189
202
|
return this._persistence.deleteAll();
|
|
190
203
|
}
|
|
191
|
-
finalizeUpload(
|
|
192
|
-
const upload = this._uploads.get(uploadId);
|
|
193
|
-
if (!upload) {
|
|
194
|
-
return undefined;
|
|
195
|
-
}
|
|
204
|
+
finalizeUpload(upload) {
|
|
196
205
|
upload.status = UploadStatus.FINISHED;
|
|
206
|
+
const metadata = this.createMetadata(upload);
|
|
197
207
|
const filePath = this.path(upload.bucketId, upload.objectId);
|
|
198
|
-
const
|
|
199
|
-
const finalMetadata = new metadata_1.StoredFileMetadata({
|
|
200
|
-
name: upload.objectId,
|
|
201
|
-
bucket: upload.bucketId,
|
|
202
|
-
contentType: "",
|
|
203
|
-
contentEncoding: upload.metadata.contentEncoding,
|
|
204
|
-
customMetadata: upload.metadata.metadata,
|
|
205
|
-
}, this._cloudFunctions, bytes, upload.metadata);
|
|
206
|
-
const file = new StoredFile(finalMetadata, filePath);
|
|
208
|
+
const file = new StoredFile(metadata, filePath);
|
|
207
209
|
this._files.set(filePath, file);
|
|
208
210
|
this._persistence.deleteFile(filePath, true);
|
|
209
211
|
this._persistence.renameFile(upload.fileLocation, filePath);
|
|
210
212
|
this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(file.metadata));
|
|
211
|
-
return
|
|
213
|
+
return file;
|
|
212
214
|
}
|
|
213
215
|
oneShotUpload(bucket, object, contentType, incomingMetadata, bytes) {
|
|
214
216
|
const filePath = this.path(bucket, object);
|
|
@@ -220,7 +222,7 @@ class StorageLayer {
|
|
|
220
222
|
contentType: incomingMetadata.contentType || "application/octet-stream",
|
|
221
223
|
contentEncoding: incomingMetadata.contentEncoding,
|
|
222
224
|
customMetadata: incomingMetadata.metadata,
|
|
223
|
-
}, this._cloudFunctions, bytes
|
|
225
|
+
}, this._cloudFunctions, bytes);
|
|
224
226
|
const file = new StoredFile(md, this._persistence.getDiskPath(filePath));
|
|
225
227
|
this._files.set(filePath, file);
|
|
226
228
|
this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(file.metadata));
|
|
@@ -242,7 +244,7 @@ class StorageLayer {
|
|
|
242
244
|
let items = [];
|
|
243
245
|
const prefixes = new Set();
|
|
244
246
|
for (const [, file] of this._files) {
|
|
245
|
-
if (file.metadata.bucket
|
|
247
|
+
if (file.metadata.bucket !== bucket) {
|
|
246
248
|
continue;
|
|
247
249
|
}
|
|
248
250
|
let name = `${delimiter}${file.metadata.name}`;
|
|
@@ -254,7 +256,7 @@ class StorageLayer {
|
|
|
254
256
|
name = name.substring(prefix.length);
|
|
255
257
|
}
|
|
256
258
|
const startAtIndex = name.indexOf(delimiter);
|
|
257
|
-
if (startAtIndex
|
|
259
|
+
if (startAtIndex === -1) {
|
|
258
260
|
if (!file.metadata.name.endsWith("/")) {
|
|
259
261
|
items.push(file.metadata.name);
|
|
260
262
|
}
|
|
@@ -266,8 +268,8 @@ class StorageLayer {
|
|
|
266
268
|
}
|
|
267
269
|
items.sort();
|
|
268
270
|
if (pageToken) {
|
|
269
|
-
const idx = items.findIndex((v) => v
|
|
270
|
-
if (idx
|
|
271
|
+
const idx = items.findIndex((v) => v === pageToken);
|
|
272
|
+
if (idx !== -1) {
|
|
271
273
|
items = items.slice(idx);
|
|
272
274
|
}
|
|
273
275
|
}
|
|
@@ -293,7 +295,7 @@ class StorageLayer {
|
|
|
293
295
|
}
|
|
294
296
|
let items = [];
|
|
295
297
|
for (const [, file] of this._files) {
|
|
296
|
-
if (file.metadata.bucket
|
|
298
|
+
if (file.metadata.bucket !== bucket) {
|
|
297
299
|
continue;
|
|
298
300
|
}
|
|
299
301
|
let name = file.metadata.name;
|
|
@@ -308,8 +310,8 @@ class StorageLayer {
|
|
|
308
310
|
}
|
|
309
311
|
items.sort();
|
|
310
312
|
if (pageToken) {
|
|
311
|
-
const idx = items.findIndex((v) => v
|
|
312
|
-
if (idx
|
|
313
|
+
const idx = items.findIndex((v) => v === pageToken);
|
|
314
|
+
if (idx !== -1) {
|
|
313
315
|
items = items.slice(idx);
|
|
314
316
|
}
|
|
315
317
|
}
|
|
@@ -33,14 +33,14 @@ class StorageEmulator {
|
|
|
33
33
|
const { host, port } = this.getInfo();
|
|
34
34
|
await this._rulesRuntime.start(this.args.auto_download);
|
|
35
35
|
this._app = await (0, server_1.createApp)(this.args.projectId, this);
|
|
36
|
-
if (typeof this.args.rules
|
|
36
|
+
if (typeof this.args.rules === "string") {
|
|
37
37
|
const rulesFile = this.args.rules;
|
|
38
38
|
this.updateRulesSource(rulesFile);
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
41
|
this._rulesetSource = this.args.rules;
|
|
42
42
|
}
|
|
43
|
-
if (!this._rulesetSource || this._rulesetSource.files.length
|
|
43
|
+
if (!this._rulesetSource || this._rulesetSource.files.length === 0) {
|
|
44
44
|
throw new error_1.FirebaseError("Can not initialize Storage emulator without a rules source / file.");
|
|
45
45
|
}
|
|
46
46
|
else if (this._rulesetSource.files.length > 1) {
|
|
@@ -149,9 +149,9 @@ class StoredFileMetadata {
|
|
|
149
149
|
if (!this.downloadTokens.length) {
|
|
150
150
|
return;
|
|
151
151
|
}
|
|
152
|
-
const remainingTokens = this.downloadTokens.filter((t) => t
|
|
152
|
+
const remainingTokens = this.downloadTokens.filter((t) => t !== token);
|
|
153
153
|
this.downloadTokens = remainingTokens;
|
|
154
|
-
if (remainingTokens.length
|
|
154
|
+
if (remainingTokens.length === 0) {
|
|
155
155
|
this.addDownloadToken();
|
|
156
156
|
}
|
|
157
157
|
this.update({});
|
|
@@ -20,7 +20,7 @@ class StorageRulesetInstance {
|
|
|
20
20
|
this.rulesetName = rulesetName;
|
|
21
21
|
}
|
|
22
22
|
async verify(opts, runtimeVariableOverrides = {}) {
|
|
23
|
-
if (opts.method
|
|
23
|
+
if (opts.method === types_1.RulesetOperationMethod.LIST && this.rulesVersion < 2) {
|
|
24
24
|
const issues = new StorageRulesIssues();
|
|
25
25
|
issues.warnings.push("Permission denied. List operations are only allowed for rules_version='2'.");
|
|
26
26
|
return {
|
|
@@ -103,6 +103,7 @@ class StorageRulesRuntime {
|
|
|
103
103
|
(_a = this._childprocess.stderr) === null || _a === void 0 ? void 0 : _a.on("data", (buf) => {
|
|
104
104
|
const error = buf.toString();
|
|
105
105
|
if (error.includes("jarfile")) {
|
|
106
|
+
emulatorLogger_1.EmulatorLogger.forEmulator(types_2.Emulators.STORAGE).log("ERROR", error);
|
|
106
107
|
throw new error_1.FirebaseError("There was an issue starting the rules emulator, please run 'firebase setup:emulators:storage` again");
|
|
107
108
|
}
|
|
108
109
|
else {
|
|
@@ -111,7 +112,7 @@ class StorageRulesRuntime {
|
|
|
111
112
|
});
|
|
112
113
|
(_b = this._childprocess.stdout) === null || _b === void 0 ? void 0 : _b.on("data", (buf) => {
|
|
113
114
|
const serializedRuntimeActionResponse = buf.toString("UTF8").trim();
|
|
114
|
-
if (serializedRuntimeActionResponse
|
|
115
|
+
if (serializedRuntimeActionResponse !== "") {
|
|
115
116
|
let rap;
|
|
116
117
|
try {
|
|
117
118
|
rap = JSON.parse(serializedRuntimeActionResponse);
|
|
@@ -217,14 +218,14 @@ class StorageRulesRuntime {
|
|
|
217
218
|
}
|
|
218
219
|
exports.StorageRulesRuntime = StorageRulesRuntime;
|
|
219
220
|
function toExpressionValue(obj) {
|
|
220
|
-
if (typeof obj
|
|
221
|
+
if (typeof obj === "string") {
|
|
221
222
|
return { string_value: obj };
|
|
222
223
|
}
|
|
223
|
-
if (typeof obj
|
|
224
|
+
if (typeof obj === "boolean") {
|
|
224
225
|
return { bool_value: obj };
|
|
225
226
|
}
|
|
226
|
-
if (typeof obj
|
|
227
|
-
if (Math.floor(obj)
|
|
227
|
+
if (typeof obj === "number") {
|
|
228
|
+
if (Math.floor(obj) === obj) {
|
|
228
229
|
return { int_value: obj };
|
|
229
230
|
}
|
|
230
231
|
else {
|
|
@@ -255,7 +256,7 @@ function toExpressionValue(obj) {
|
|
|
255
256
|
null_value: 0,
|
|
256
257
|
};
|
|
257
258
|
}
|
|
258
|
-
if (typeof obj
|
|
259
|
+
if (typeof obj === "object") {
|
|
259
260
|
const fields = {};
|
|
260
261
|
Object.keys(obj).forEach((key) => {
|
|
261
262
|
fields[key] = toExpressionValue(obj[key]);
|
package/lib/emulator/types.js
CHANGED
|
@@ -13,6 +13,7 @@ var Emulators;
|
|
|
13
13
|
Emulators["UI"] = "ui";
|
|
14
14
|
Emulators["LOGGING"] = "logging";
|
|
15
15
|
Emulators["STORAGE"] = "storage";
|
|
16
|
+
Emulators["EXTENSIONS"] = "extensions";
|
|
16
17
|
})(Emulators = exports.Emulators || (exports.Emulators = {}));
|
|
17
18
|
exports.DOWNLOADABLE_EMULATORS = [
|
|
18
19
|
Emulators.FIRESTORE,
|
|
@@ -48,6 +49,7 @@ exports.EMULATORS_SUPPORTED_BY_UI = [
|
|
|
48
49
|
Emulators.FIRESTORE,
|
|
49
50
|
Emulators.FUNCTIONS,
|
|
50
51
|
Emulators.STORAGE,
|
|
52
|
+
Emulators.EXTENSIONS,
|
|
51
53
|
];
|
|
52
54
|
exports.EMULATORS_SUPPORTED_BY_USE_EMULATOR = [
|
|
53
55
|
Emulators.AUTH,
|
|
@@ -59,6 +61,7 @@ exports.ALL_EMULATORS = [
|
|
|
59
61
|
Emulators.HUB,
|
|
60
62
|
Emulators.UI,
|
|
61
63
|
Emulators.LOGGING,
|
|
64
|
+
Emulators.EXTENSIONS,
|
|
62
65
|
...exports.ALL_SERVICE_EMULATORS,
|
|
63
66
|
];
|
|
64
67
|
function isDownloadableEmulator(value) {
|
package/lib/ensureApiEnabled.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensure = exports.check = exports.POLL_SETTINGS = void 0;
|
|
3
|
+
exports.enableApiURI = exports.ensure = exports.check = exports.POLL_SETTINGS = void 0;
|
|
4
4
|
const cli_color_1 = require("cli-color");
|
|
5
5
|
const track = require("./track");
|
|
6
6
|
const api_1 = require("./api");
|
|
@@ -75,3 +75,7 @@ async function ensure(projectId, apiName, prefix, silent = false) {
|
|
|
75
75
|
return enableApiWithRetries(projectId, apiName, prefix, silent);
|
|
76
76
|
}
|
|
77
77
|
exports.ensure = ensure;
|
|
78
|
+
function enableApiURI(projectId, apiName) {
|
|
79
|
+
return `https://console.cloud.google.com/apis/library/${apiName}?project=${projectId}`;
|
|
80
|
+
}
|
|
81
|
+
exports.enableApiURI = enableApiURI;
|
package/lib/error.js
CHANGED
|
@@ -23,7 +23,7 @@ function isBillingError(e) {
|
|
|
23
23
|
return !!((_d = (_c = (_b = (_a = e.context) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.details) === null || _d === void 0 ? void 0 : _d.find((d) => {
|
|
24
24
|
var _a;
|
|
25
25
|
return (((_a = d.violations) === null || _a === void 0 ? void 0 : _a.find((v) => v.type === "serviceusage/billing-enabled")) ||
|
|
26
|
-
d.reason
|
|
26
|
+
d.reason === "UREQ_PROJECT_BILLING_NOT_FOUND");
|
|
27
27
|
}));
|
|
28
28
|
}
|
|
29
29
|
exports.isBillingError = isBillingError;
|
|
@@ -20,7 +20,7 @@ var SecretUpdateAction;
|
|
|
20
20
|
function checkResponse(response, spec) {
|
|
21
21
|
let valid = true;
|
|
22
22
|
let responses;
|
|
23
|
-
if (spec.required && (response
|
|
23
|
+
if (spec.required && (response === "" || response === undefined)) {
|
|
24
24
|
utils.logWarning(`Param ${spec.param} is required, but no value was provided.`);
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
@@ -199,7 +199,7 @@ async function generateSecretName(projectId, instanceId, paramName) {
|
|
|
199
199
|
return secretName;
|
|
200
200
|
}
|
|
201
201
|
async function addNewSecretVersion(projectId, instanceId, secret, paramSpec, secretValue) {
|
|
202
|
-
const version = await secretManagerApi.addVersion(secret, secretValue);
|
|
202
|
+
const version = await secretManagerApi.addVersion(projectId, secret.name, secretValue);
|
|
203
203
|
await secretsUtils.grantFirexServiceAgentSecretAdminRole(secret);
|
|
204
204
|
return `projects/${version.secret.projectId}/secrets/${version.secret.name}/versions/${version.versionId}`;
|
|
205
205
|
}
|
|
@@ -58,7 +58,9 @@ function breakingChangesInUpdate(versionsInUpdate) {
|
|
|
58
58
|
const semvers = versionsInUpdate.map((v) => semver.parse(v)).sort(semver.compare);
|
|
59
59
|
for (let i = 1; i < semvers.length; i++) {
|
|
60
60
|
const hasMajorBump = semvers[i - 1].major < semvers[i].major;
|
|
61
|
-
const hasMinorBumpInPreview = semvers[i - 1].major
|
|
61
|
+
const hasMinorBumpInPreview = semvers[i - 1].major === 0 &&
|
|
62
|
+
semvers[i].major === 0 &&
|
|
63
|
+
semvers[i - 1].minor < semvers[i].minor;
|
|
62
64
|
if (hasMajorBump || hasMinorBumpInPreview) {
|
|
63
65
|
breakingVersions.push(semvers[i].raw);
|
|
64
66
|
}
|
|
@@ -49,7 +49,7 @@ Please select the one that you would like to associate with this project:`,
|
|
|
49
49
|
billingEnabled = await openBillingAccount(projectId, billingURL, true);
|
|
50
50
|
}
|
|
51
51
|
else {
|
|
52
|
-
const billingAccount = accounts.find((a) => a.displayName
|
|
52
|
+
const billingAccount = accounts.find((a) => a.displayName === answer);
|
|
53
53
|
billingEnabled = await cloudbilling.setBillingAccount(projectId, billingAccount.name);
|
|
54
54
|
}
|
|
55
55
|
return logBillingStatus(billingEnabled, projectId);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.diagnose = void 0;
|
|
4
|
+
const extensionsHelper_1 = require("./extensionsHelper");
|
|
5
|
+
const getProjectNumber_1 = require("../getProjectNumber");
|
|
6
|
+
const utils = require("../utils");
|
|
7
|
+
const resourceManager = require("../gcp/resourceManager");
|
|
8
|
+
const extensionsApi_1 = require("./extensionsApi");
|
|
9
|
+
const prompt_1 = require("../prompt");
|
|
10
|
+
const logger_1 = require("../logger");
|
|
11
|
+
const error_1 = require("../error");
|
|
12
|
+
const SERVICE_AGENT_ROLE = "roles/firebasemods.serviceAgent";
|
|
13
|
+
async function diagnose(projectId) {
|
|
14
|
+
const projectNumber = await (0, getProjectNumber_1.getProjectNumber)({ projectId });
|
|
15
|
+
const firexSaProjectId = utils.envOverride("FIREBASE_EXTENSIONS_SA_PROJECT_ID", "gcp-sa-firebasemods");
|
|
16
|
+
const saEmail = `service-${projectNumber}@${firexSaProjectId}.iam.gserviceaccount.com`;
|
|
17
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, "Checking project IAM policy...");
|
|
18
|
+
await (0, extensionsApi_1.listInstances)(projectId);
|
|
19
|
+
let policy;
|
|
20
|
+
try {
|
|
21
|
+
policy = await resourceManager.getIamPolicy(projectId);
|
|
22
|
+
logger_1.logger.debug(policy);
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
if (e instanceof error_1.FirebaseError && e.status === 403) {
|
|
26
|
+
throw new error_1.FirebaseError("Unable to get project IAM policy, permission denied (403). Please " +
|
|
27
|
+
"make sure you have sufficient project privileges or if this is a brand new project " +
|
|
28
|
+
"try again in a few minutes.");
|
|
29
|
+
}
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
32
|
+
if (policy.bindings.find((b) => b.role === SERVICE_AGENT_ROLE && b.members.includes("serviceAccount:" + saEmail))) {
|
|
33
|
+
utils.logLabeledSuccess(extensionsHelper_1.logPrefix, "Project IAM policy OK");
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
utils.logWarning("Firebase Extensions Service Agent is missing a required IAM role " +
|
|
38
|
+
"`Firebase Extensions API Service Agent`.");
|
|
39
|
+
const fix = await (0, prompt_1.promptOnce)({
|
|
40
|
+
type: "confirm",
|
|
41
|
+
message: "Would you like to fix the issue by updating IAM policy to include Firebase " +
|
|
42
|
+
"Extensions Service Agent with role `Firebase Extensions API Service Agent`",
|
|
43
|
+
});
|
|
44
|
+
if (fix) {
|
|
45
|
+
policy.bindings.push({
|
|
46
|
+
role: SERVICE_AGENT_ROLE,
|
|
47
|
+
members: ["serviceAccount:" + saEmail],
|
|
48
|
+
});
|
|
49
|
+
await resourceManager.setIamPolicy(projectId, policy, "bindings");
|
|
50
|
+
utils.logSuccess("Project IAM policy updated successfully");
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.diagnose = diagnose;
|
|
@@ -136,7 +136,7 @@ async function displayUpdateChangesRequiringConfirmation(args) {
|
|
|
136
136
|
}
|
|
137
137
|
exports.displayUpdateChangesRequiringConfirmation = displayUpdateChangesRequiringConfirmation;
|
|
138
138
|
function compareResources(resource1, resource2) {
|
|
139
|
-
return resource1.name
|
|
139
|
+
return resource1.name === resource2.name && resource1.type === resource2.type;
|
|
140
140
|
}
|
|
141
141
|
function getResourceReadableName(resource) {
|
|
142
142
|
return resource.type === "firebaseextensions.v1beta.function"
|