firebase-tools 10.2.2 → 10.3.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/lib/commands/ext-configure.js +58 -4
- package/lib/commands/ext-export.js +4 -9
- package/lib/commands/ext-install.js +63 -4
- package/lib/commands/ext-uninstall.js +9 -0
- package/lib/commands/ext-update.js +55 -2
- package/lib/config.js +6 -3
- package/lib/deploy/extensions/planner.js +6 -6
- package/lib/deploy/functions/backend.js +10 -1
- package/lib/deploy/functions/checkIam.js +4 -4
- package/lib/deploy/functions/prepare.js +2 -1
- package/lib/deploy/functions/release/fabricator.js +4 -4
- package/lib/deploy/functions/release/planner.js +34 -20
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +6 -1
- package/lib/deploy/functions/runtimes/node/index.js +27 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +24 -8
- package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
- package/lib/deploy/functions/services/index.js +9 -1
- package/lib/deploy/functions/services/storage.js +10 -4
- package/lib/deploy/functions/triggerRegionHelper.js +1 -1
- package/lib/emulator/constants.js +1 -0
- package/lib/emulator/controller.js +9 -7
- package/lib/emulator/extensions/validation.js +37 -2
- package/lib/emulator/extensionsEmulator.js +44 -9
- package/lib/emulator/functionsEmulator.js +13 -8
- package/lib/emulator/functionsEmulatorShared.js +17 -10
- package/lib/emulator/storage/apis/firebase.js +312 -335
- package/lib/emulator/storage/apis/gcloud.js +238 -113
- package/lib/emulator/storage/crc.js +5 -1
- package/lib/emulator/storage/errors.js +9 -0
- package/lib/emulator/storage/files.js +161 -304
- package/lib/emulator/storage/index.js +27 -73
- package/lib/emulator/storage/metadata.js +63 -49
- package/lib/emulator/storage/multipart.js +62 -0
- package/lib/emulator/storage/persistence.js +78 -0
- package/lib/emulator/storage/rules/config.js +33 -0
- package/lib/emulator/storage/rules/manager.js +81 -0
- package/lib/emulator/storage/rules/utils.js +48 -0
- package/lib/emulator/storage/server.js +2 -2
- package/lib/emulator/storage/upload.js +106 -0
- package/lib/extensions/emulator/optionsHelper.js +35 -3
- package/lib/extensions/extensionsHelper.js +19 -10
- package/lib/extensions/manifest.js +109 -13
- package/lib/extensions/paramHelper.js +5 -4
- package/lib/functions/env.js +4 -6
- package/lib/functions/events/v2.js +11 -0
- package/lib/gcp/cloudfunctions.js +18 -6
- package/lib/gcp/cloudfunctionsv2.js +30 -12
- package/lib/gcp/resourceManager.js +4 -4
- package/lib/serve/functions.js +2 -1
- package/lib/utils.js +14 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/lib/deploy/extensions/params.js +0 -42
- package/lib/deploy/functions/eventTypes.js +0 -10
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UploadService = exports.NotCancellableError = exports.UploadNotActiveError = exports.UploadStatus = exports.UploadType = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
var UploadType;
|
|
7
|
+
(function (UploadType) {
|
|
8
|
+
UploadType[UploadType["MULTIPART"] = 0] = "MULTIPART";
|
|
9
|
+
UploadType[UploadType["RESUMABLE"] = 1] = "RESUMABLE";
|
|
10
|
+
})(UploadType = exports.UploadType || (exports.UploadType = {}));
|
|
11
|
+
var UploadStatus;
|
|
12
|
+
(function (UploadStatus) {
|
|
13
|
+
UploadStatus[UploadStatus["ACTIVE"] = 0] = "ACTIVE";
|
|
14
|
+
UploadStatus[UploadStatus["CANCELLED"] = 1] = "CANCELLED";
|
|
15
|
+
UploadStatus[UploadStatus["FINISHED"] = 2] = "FINISHED";
|
|
16
|
+
})(UploadStatus = exports.UploadStatus || (exports.UploadStatus = {}));
|
|
17
|
+
class UploadNotActiveError extends Error {
|
|
18
|
+
}
|
|
19
|
+
exports.UploadNotActiveError = UploadNotActiveError;
|
|
20
|
+
class NotCancellableError extends Error {
|
|
21
|
+
}
|
|
22
|
+
exports.NotCancellableError = NotCancellableError;
|
|
23
|
+
class UploadService {
|
|
24
|
+
constructor(_persistence) {
|
|
25
|
+
this._persistence = _persistence;
|
|
26
|
+
this.reset();
|
|
27
|
+
}
|
|
28
|
+
reset() {
|
|
29
|
+
this._uploads = new Map();
|
|
30
|
+
}
|
|
31
|
+
multipartUpload(request) {
|
|
32
|
+
const upload = this.startMultipartUpload(request, request.dataRaw.byteLength);
|
|
33
|
+
this._persistence.deleteFile(upload.path, true);
|
|
34
|
+
this._persistence.appendBytes(upload.path, request.dataRaw);
|
|
35
|
+
return upload;
|
|
36
|
+
}
|
|
37
|
+
startMultipartUpload(request, sizeInBytes) {
|
|
38
|
+
const id = (0, uuid_1.v4)();
|
|
39
|
+
const upload = {
|
|
40
|
+
id: (0, uuid_1.v4)(),
|
|
41
|
+
bucketId: request.bucketId,
|
|
42
|
+
objectId: request.objectId,
|
|
43
|
+
type: UploadType.MULTIPART,
|
|
44
|
+
path: this.getStagingFileName(id, request.bucketId, request.objectId),
|
|
45
|
+
status: UploadStatus.FINISHED,
|
|
46
|
+
metadata: JSON.parse(request.metadataRaw),
|
|
47
|
+
size: sizeInBytes,
|
|
48
|
+
authorization: request.authorization,
|
|
49
|
+
};
|
|
50
|
+
this._uploads.set(upload.id, upload);
|
|
51
|
+
return upload;
|
|
52
|
+
}
|
|
53
|
+
startResumableUpload(request) {
|
|
54
|
+
const id = (0, uuid_1.v4)();
|
|
55
|
+
const upload = {
|
|
56
|
+
id: id,
|
|
57
|
+
bucketId: request.bucketId,
|
|
58
|
+
objectId: request.objectId,
|
|
59
|
+
type: UploadType.RESUMABLE,
|
|
60
|
+
path: this.getStagingFileName(id, request.bucketId, request.objectId),
|
|
61
|
+
status: UploadStatus.ACTIVE,
|
|
62
|
+
metadata: JSON.parse(request.metadataRaw),
|
|
63
|
+
size: 0,
|
|
64
|
+
authorization: request.authorization,
|
|
65
|
+
};
|
|
66
|
+
this._uploads.set(upload.id, upload);
|
|
67
|
+
this._persistence.deleteFile(upload.path, true);
|
|
68
|
+
return upload;
|
|
69
|
+
}
|
|
70
|
+
continueResumableUpload(uploadId, dataRaw) {
|
|
71
|
+
const upload = this.getResumableUpload(uploadId);
|
|
72
|
+
if (upload.status !== UploadStatus.ACTIVE) {
|
|
73
|
+
throw new UploadNotActiveError();
|
|
74
|
+
}
|
|
75
|
+
this._persistence.appendBytes(upload.path, dataRaw);
|
|
76
|
+
upload.size += dataRaw.byteLength;
|
|
77
|
+
return upload;
|
|
78
|
+
}
|
|
79
|
+
getResumableUpload(uploadId) {
|
|
80
|
+
const upload = this._uploads.get(uploadId);
|
|
81
|
+
if (!upload || upload.type !== UploadType.RESUMABLE) {
|
|
82
|
+
throw new errors_1.NotFoundError();
|
|
83
|
+
}
|
|
84
|
+
return upload;
|
|
85
|
+
}
|
|
86
|
+
cancelResumableUpload(uploadId) {
|
|
87
|
+
const upload = this.getResumableUpload(uploadId);
|
|
88
|
+
if (upload.status === UploadStatus.FINISHED) {
|
|
89
|
+
throw new NotCancellableError();
|
|
90
|
+
}
|
|
91
|
+
upload.status = UploadStatus.CANCELLED;
|
|
92
|
+
return upload;
|
|
93
|
+
}
|
|
94
|
+
finalizeResumableUpload(uploadId) {
|
|
95
|
+
const upload = this.getResumableUpload(uploadId);
|
|
96
|
+
if (upload.status === UploadStatus.CANCELLED) {
|
|
97
|
+
throw new UploadNotActiveError();
|
|
98
|
+
}
|
|
99
|
+
upload.status = UploadStatus.FINISHED;
|
|
100
|
+
return upload;
|
|
101
|
+
}
|
|
102
|
+
getStagingFileName(uploadId, bucketId, objectId) {
|
|
103
|
+
return encodeURIComponent(`${uploadId}_b_${bucketId}_o_${objectId}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.UploadService = UploadService;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getParams = exports.getExtensionFunctionInfo = exports.buildOptions = void 0;
|
|
3
|
+
exports.getParams = exports.getSecretEnvVars = exports.getNonSecretEnv = exports.getExtensionFunctionInfo = exports.buildOptions = void 0;
|
|
4
4
|
const fs = require("fs-extra");
|
|
5
5
|
const _ = require("lodash");
|
|
6
6
|
const path = require("path");
|
|
@@ -8,6 +8,7 @@ const paramHelper = require("../paramHelper");
|
|
|
8
8
|
const specHelper = require("./specHelper");
|
|
9
9
|
const localHelper = require("../localHelper");
|
|
10
10
|
const triggerHelper = require("./triggerHelper");
|
|
11
|
+
const extensionsApi_1 = require("../extensionsApi");
|
|
11
12
|
const extensionsHelper = require("../extensionsHelper");
|
|
12
13
|
const config_1 = require("../../config");
|
|
13
14
|
const error_1 = require("../../error");
|
|
@@ -35,9 +36,9 @@ async function buildOptions(options) {
|
|
|
35
36
|
return options;
|
|
36
37
|
}
|
|
37
38
|
exports.buildOptions = buildOptions;
|
|
38
|
-
async function getExtensionFunctionInfo(extensionDir, instanceId,
|
|
39
|
+
async function getExtensionFunctionInfo(extensionDir, instanceId, paramValues) {
|
|
39
40
|
const spec = await specHelper.readExtensionYaml(extensionDir);
|
|
40
|
-
const functionResources = specHelper.getFunctionResourcesWithParamSubstitution(spec,
|
|
41
|
+
const functionResources = specHelper.getFunctionResourcesWithParamSubstitution(spec, paramValues);
|
|
41
42
|
const extensionTriggers = functionResources
|
|
42
43
|
.map((r) => triggerHelper.functionResourceToEmulatedTriggerDefintion(r))
|
|
43
44
|
.map((trigger) => {
|
|
@@ -45,12 +46,43 @@ async function getExtensionFunctionInfo(extensionDir, instanceId, params) {
|
|
|
45
46
|
return trigger;
|
|
46
47
|
});
|
|
47
48
|
const nodeMajorVersion = specHelper.getNodeVersion(functionResources);
|
|
49
|
+
const nonSecretEnv = getNonSecretEnv(spec.params, paramValues);
|
|
50
|
+
const secretEnvVariables = getSecretEnvVars(spec.params, paramValues);
|
|
48
51
|
return {
|
|
49
52
|
extensionTriggers,
|
|
50
53
|
nodeMajorVersion,
|
|
54
|
+
nonSecretEnv,
|
|
55
|
+
secretEnvVariables,
|
|
51
56
|
};
|
|
52
57
|
}
|
|
53
58
|
exports.getExtensionFunctionInfo = getExtensionFunctionInfo;
|
|
59
|
+
const isSecretParam = (p) => p.type === extensionsHelper.SpecParamType.SECRET || p.type === extensionsApi_1.ParamType.SECRET;
|
|
60
|
+
function getNonSecretEnv(params, paramValues) {
|
|
61
|
+
const getNonSecretEnv = Object.assign({}, paramValues);
|
|
62
|
+
const secretParams = params.filter(isSecretParam);
|
|
63
|
+
for (const p of secretParams) {
|
|
64
|
+
delete getNonSecretEnv[p.param];
|
|
65
|
+
}
|
|
66
|
+
return getNonSecretEnv;
|
|
67
|
+
}
|
|
68
|
+
exports.getNonSecretEnv = getNonSecretEnv;
|
|
69
|
+
function getSecretEnvVars(params, paramValues) {
|
|
70
|
+
const secretEnvVar = [];
|
|
71
|
+
const secretParams = params.filter(isSecretParam);
|
|
72
|
+
for (const s of secretParams) {
|
|
73
|
+
if (paramValues[s.param]) {
|
|
74
|
+
const [, projectId, , secret, , version] = paramValues[s.param].split("/");
|
|
75
|
+
secretEnvVar.push({
|
|
76
|
+
key: s.param,
|
|
77
|
+
secret,
|
|
78
|
+
projectId,
|
|
79
|
+
version,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return secretEnvVar;
|
|
84
|
+
}
|
|
85
|
+
exports.getSecretEnvVars = getSecretEnvVars;
|
|
54
86
|
function getParams(options, extensionSpec) {
|
|
55
87
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
56
88
|
const userParams = paramHelper.readEnvFile(options.testParams);
|
|
@@ -14,6 +14,7 @@ const api_1 = require("../api");
|
|
|
14
14
|
const archiveDirectory_1 = require("../archiveDirectory");
|
|
15
15
|
const utils_1 = require("./utils");
|
|
16
16
|
const functionsConfig_1 = require("../functionsConfig");
|
|
17
|
+
const adminSdkConfig_1 = require("../emulator/adminSdkConfig");
|
|
17
18
|
const resolveSource_1 = require("./resolveSource");
|
|
18
19
|
const error_1 = require("../error");
|
|
19
20
|
const diagnose_1 = require("./diagnose");
|
|
@@ -29,6 +30,7 @@ const logger_1 = require("../logger");
|
|
|
29
30
|
const utils_2 = require("../utils");
|
|
30
31
|
const changelog_1 = require("./changelog");
|
|
31
32
|
const getProjectNumber_1 = require("../getProjectNumber");
|
|
33
|
+
const constants_1 = require("../emulator/constants");
|
|
32
34
|
var SpecParamType;
|
|
33
35
|
(function (SpecParamType) {
|
|
34
36
|
SpecParamType["SELECT"] = "select";
|
|
@@ -76,21 +78,28 @@ function getDBInstanceFromURL(databaseUrl = "") {
|
|
|
76
78
|
return "";
|
|
77
79
|
}
|
|
78
80
|
exports.getDBInstanceFromURL = getDBInstanceFromURL;
|
|
79
|
-
async function getFirebaseProjectParams(projectId) {
|
|
80
|
-
|
|
81
|
-
const
|
|
81
|
+
async function getFirebaseProjectParams(projectId, emulatorMode = false) {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
const body = emulatorMode
|
|
84
|
+
? await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(projectId)
|
|
85
|
+
: await (0, functionsConfig_1.getFirebaseConfig)({ project: projectId });
|
|
86
|
+
const projectNumber = emulatorMode && constants_1.Constants.isDemoProject(projectId)
|
|
87
|
+
? constants_1.Constants.FAKE_PROJECT_NUMBER
|
|
88
|
+
: await (0, getProjectNumber_1.getProjectNumber)({ projectId });
|
|
89
|
+
const databaseURL = (_a = body === null || body === void 0 ? void 0 : body.databaseURL) !== null && _a !== void 0 ? _a : `https://${projectId}.firebaseio.com`;
|
|
90
|
+
const storageBucket = (_b = body === null || body === void 0 ? void 0 : body.storageBucket) !== null && _b !== void 0 ? _b : `${projectId}.appspot.com`;
|
|
82
91
|
const FIREBASE_CONFIG = JSON.stringify({
|
|
83
|
-
projectId
|
|
84
|
-
databaseURL
|
|
85
|
-
storageBucket
|
|
92
|
+
projectId,
|
|
93
|
+
databaseURL,
|
|
94
|
+
storageBucket,
|
|
86
95
|
});
|
|
87
96
|
return {
|
|
88
|
-
PROJECT_ID:
|
|
97
|
+
PROJECT_ID: projectId,
|
|
89
98
|
PROJECT_NUMBER: projectNumber,
|
|
90
|
-
DATABASE_URL:
|
|
91
|
-
STORAGE_BUCKET:
|
|
99
|
+
DATABASE_URL: databaseURL,
|
|
100
|
+
STORAGE_BUCKET: storageBucket,
|
|
92
101
|
FIREBASE_CONFIG,
|
|
93
|
-
DATABASE_INSTANCE: getDBInstanceFromURL(
|
|
102
|
+
DATABASE_INSTANCE: getDBInstanceFromURL(databaseURL),
|
|
94
103
|
};
|
|
95
104
|
}
|
|
96
105
|
exports.getFirebaseProjectParams = getFirebaseProjectParams;
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.writeToManifest = void 0;
|
|
3
|
+
exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeToManifest = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
|
+
const path = require("path");
|
|
5
6
|
const refs = require("./refs");
|
|
7
|
+
const config_1 = require("../config");
|
|
6
8
|
const logger_1 = require("../logger");
|
|
7
9
|
const prompt_1 = require("../prompt");
|
|
8
|
-
|
|
10
|
+
const paramHelper_1 = require("./paramHelper");
|
|
11
|
+
const error_1 = require("../error");
|
|
12
|
+
const utils = require("../utils");
|
|
13
|
+
const extensionsHelper_1 = require("./extensionsHelper");
|
|
14
|
+
const ENV_DIRECTORY = "extensions";
|
|
15
|
+
async function writeToManifest(specs, config, options, allowOverwrite = false) {
|
|
9
16
|
if (config.has("extensions") &&
|
|
10
17
|
Object.keys(config.get("extensions")).length &&
|
|
11
18
|
!options.nonInteractive &&
|
|
@@ -13,36 +20,125 @@ async function writeToManifest(specs, config, options) {
|
|
|
13
20
|
const currentExtensions = Object.entries(config.get("extensions"))
|
|
14
21
|
.map((i) => `${i[0]}: ${i[1]}`)
|
|
15
22
|
.join("\n\t");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (allowOverwrite) {
|
|
24
|
+
const overwrite = await (0, prompt_1.promptOnce)({
|
|
25
|
+
type: "list",
|
|
26
|
+
message: `firebase.json already contains extensions:\n${currentExtensions}\nWould you like to overwrite or merge?`,
|
|
27
|
+
choices: [
|
|
28
|
+
{ name: "Overwrite", value: true },
|
|
29
|
+
{ name: "Merge", value: false },
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
if (overwrite) {
|
|
33
|
+
config.set("extensions", {});
|
|
34
|
+
}
|
|
26
35
|
}
|
|
27
36
|
}
|
|
28
37
|
writeExtensionsToFirebaseJson(specs, config);
|
|
29
38
|
await writeEnvFiles(specs, config, options.force);
|
|
30
39
|
}
|
|
31
40
|
exports.writeToManifest = writeToManifest;
|
|
41
|
+
function removeFromManifest(instanceId, config) {
|
|
42
|
+
if (!instanceExists(instanceId, config)) {
|
|
43
|
+
throw new error_1.FirebaseError(`Extension instance ${instanceId} not found in firebase.json.`);
|
|
44
|
+
}
|
|
45
|
+
const extensions = config.get("extensions", {});
|
|
46
|
+
extensions[instanceId] = undefined;
|
|
47
|
+
config.set("extensions", extensions);
|
|
48
|
+
config.writeProjectFile("firebase.json", config.src);
|
|
49
|
+
logger_1.logger.info(`Removed extension instance ${instanceId} from firebase.json`);
|
|
50
|
+
config.deleteProjectFile(`extensions/${instanceId}.env`);
|
|
51
|
+
logger_1.logger.info(`Removed extension instance environment config extensions/${instanceId}.env`);
|
|
52
|
+
config.deleteProjectFile(`extensions/${instanceId}.env.local`);
|
|
53
|
+
logger_1.logger.info(`Removed extension instance environment config extensions/${instanceId}.env.local`);
|
|
54
|
+
}
|
|
55
|
+
exports.removeFromManifest = removeFromManifest;
|
|
56
|
+
function loadConfig(options) {
|
|
57
|
+
const existingConfig = config_1.Config.load(options, true);
|
|
58
|
+
if (!existingConfig) {
|
|
59
|
+
throw new error_1.FirebaseError("Not currently in a Firebase directory. Run `firebase init` to create a Firebase directory.");
|
|
60
|
+
}
|
|
61
|
+
return existingConfig;
|
|
62
|
+
}
|
|
63
|
+
exports.loadConfig = loadConfig;
|
|
64
|
+
function instanceExists(instanceId, config) {
|
|
65
|
+
return !!config.get("extensions", {})[instanceId];
|
|
66
|
+
}
|
|
67
|
+
exports.instanceExists = instanceExists;
|
|
68
|
+
function getInstanceRef(instanceId, config) {
|
|
69
|
+
if (!instanceExists(instanceId, config)) {
|
|
70
|
+
throw new error_1.FirebaseError(`Could not find extension instance ${instanceId} in firebase.json`);
|
|
71
|
+
}
|
|
72
|
+
const ref = config.get("extensions", {})[instanceId];
|
|
73
|
+
return refs.parse(ref);
|
|
74
|
+
}
|
|
75
|
+
exports.getInstanceRef = getInstanceRef;
|
|
32
76
|
function writeExtensionsToFirebaseJson(specs, config) {
|
|
33
77
|
const extensions = config.get("extensions", {});
|
|
34
78
|
for (const s of specs) {
|
|
35
79
|
extensions[s.instanceId] = refs.toExtensionVersionRef(s.ref);
|
|
36
80
|
}
|
|
37
81
|
config.set("extensions", extensions);
|
|
38
|
-
logger_1.logger.info("Adding Extensions to " + clc.bold("firebase.json") + "...");
|
|
39
82
|
config.writeProjectFile("firebase.json", config.src);
|
|
83
|
+
utils.logSuccess("Wrote extensions to " + clc.bold("firebase.json") + "...");
|
|
40
84
|
}
|
|
41
85
|
async function writeEnvFiles(specs, config, force) {
|
|
42
86
|
for (const spec of specs) {
|
|
43
87
|
const content = Object.entries(spec.params)
|
|
88
|
+
.sort((a, b) => {
|
|
89
|
+
return a[0].localeCompare(b[0]);
|
|
90
|
+
})
|
|
44
91
|
.map((r) => `${r[0]}=${r[1]}`)
|
|
45
92
|
.join("\n");
|
|
46
93
|
await config.askWriteProjectFile(`extensions/${spec.instanceId}.env`, content, force);
|
|
47
94
|
}
|
|
48
95
|
}
|
|
96
|
+
function readInstanceParam(args) {
|
|
97
|
+
var _a;
|
|
98
|
+
const aliases = (_a = args.aliases) !== null && _a !== void 0 ? _a : [];
|
|
99
|
+
const filesToCheck = [
|
|
100
|
+
`${args.instanceId}.env`,
|
|
101
|
+
...aliases.map((alias) => `${args.instanceId}.env.${alias}`),
|
|
102
|
+
...(args.projectNumber ? [`${args.instanceId}.env.${args.projectNumber}`] : []),
|
|
103
|
+
...(args.projectId ? [`${args.instanceId}.env.${args.projectId}`] : []),
|
|
104
|
+
];
|
|
105
|
+
if (args.checkLocal) {
|
|
106
|
+
filesToCheck.push(`${args.instanceId}.env.local`);
|
|
107
|
+
}
|
|
108
|
+
let noFilesFound = true;
|
|
109
|
+
const combinedParams = {};
|
|
110
|
+
for (const fileToCheck of filesToCheck) {
|
|
111
|
+
try {
|
|
112
|
+
const params = readParamsFile(args.projectDir, fileToCheck);
|
|
113
|
+
logger_1.logger.debug(`Successfully read params from ${fileToCheck}`);
|
|
114
|
+
noFilesFound = false;
|
|
115
|
+
Object.assign(combinedParams, params);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
logger_1.logger.debug(`${err}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (noFilesFound) {
|
|
122
|
+
throw new error_1.FirebaseError(`No params file found for ${args.instanceId}`);
|
|
123
|
+
}
|
|
124
|
+
return combinedParams;
|
|
125
|
+
}
|
|
126
|
+
exports.readInstanceParam = readInstanceParam;
|
|
127
|
+
function readParamsFile(projectDir, fileName) {
|
|
128
|
+
const paramPath = path.join(projectDir, ENV_DIRECTORY, fileName);
|
|
129
|
+
const params = (0, paramHelper_1.readEnvFile)(paramPath);
|
|
130
|
+
return params;
|
|
131
|
+
}
|
|
132
|
+
function showDeprecationWarning() {
|
|
133
|
+
utils.logLabeledWarning(extensionsHelper_1.logPrefix, "The behavior of ext:install, ext:update, ext:configure, and ext:uninstall will change in firebase-tools@11.0.0. " +
|
|
134
|
+
"Instead of deploying extensions directly, " +
|
|
135
|
+
"changes to extension instances will be written to firebase.json and ./extensions/*.env. " +
|
|
136
|
+
`Then ${clc.bold("firebase deploy (--only extensions)")} will deploy the changes to your Firebase project. ` +
|
|
137
|
+
`To access this behavior now, pass the ${clc.bold("--local")} flag.`);
|
|
138
|
+
}
|
|
139
|
+
exports.showDeprecationWarning = showDeprecationWarning;
|
|
140
|
+
function showPreviewWarning() {
|
|
141
|
+
utils.logLabeledWarning(extensionsHelper_1.logPrefix, "These changes will be reflected in your Firebase Emulator after restart. " +
|
|
142
|
+
`Run ${clc.bold("firebase deploy (--only extensions)")} to deploy the changes to your Firebase project. `);
|
|
143
|
+
}
|
|
144
|
+
exports.showPreviewWarning = showPreviewWarning;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readEnvFile = exports.
|
|
3
|
+
exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const clc = require("cli-color");
|
|
@@ -19,6 +19,7 @@ function setNewDefaults(params, newDefaults) {
|
|
|
19
19
|
});
|
|
20
20
|
return params;
|
|
21
21
|
}
|
|
22
|
+
exports.setNewDefaults = setNewDefaults;
|
|
22
23
|
function getParamsWithCurrentValuesAsDefaults(extensionInstance) {
|
|
23
24
|
const specParams = _.cloneDeep(_.get(extensionInstance, "config.source.spec.params", []));
|
|
24
25
|
const currentParams = _.cloneDeep(_.get(extensionInstance, "config.params", {}));
|
|
@@ -91,9 +92,10 @@ async function promptForNewParams(args) {
|
|
|
91
92
|
const comparer = (param1, param2) => {
|
|
92
93
|
return param1.type === param2.type && param1.param === param2.param;
|
|
93
94
|
};
|
|
94
|
-
|
|
95
|
+
const oldParams = args.spec.params.filter((p) => Object.keys(args.currentParams).includes(p.param));
|
|
96
|
+
let paramsDiffDeletions = _.differenceWith(oldParams, args.newSpec.params, comparer);
|
|
95
97
|
paramsDiffDeletions = (0, extensionsHelper_1.substituteParams)(paramsDiffDeletions, firebaseProjectParams);
|
|
96
|
-
let paramsDiffAdditions = _.differenceWith(args.newSpec.params,
|
|
98
|
+
let paramsDiffAdditions = _.differenceWith(args.newSpec.params, oldParams, comparer);
|
|
97
99
|
paramsDiffAdditions = (0, extensionsHelper_1.substituteParams)(paramsDiffAdditions, firebaseProjectParams);
|
|
98
100
|
if (paramsDiffDeletions.length) {
|
|
99
101
|
logger_1.logger.info("The following params will no longer be used:");
|
|
@@ -127,7 +129,6 @@ function getParamsFromFile(args) {
|
|
|
127
129
|
logger_1.logger.info(`Using param values from ${args.paramsEnvPath}`);
|
|
128
130
|
return params;
|
|
129
131
|
}
|
|
130
|
-
exports.getParamsFromFile = getParamsFromFile;
|
|
131
132
|
function readEnvFile(envPath) {
|
|
132
133
|
const buf = fs.readFileSync(path.resolve(envPath), "utf8");
|
|
133
134
|
const result = env.parse(buf.toString().trim());
|
package/lib/functions/env.js
CHANGED
|
@@ -121,15 +121,13 @@ function parseStrict(data) {
|
|
|
121
121
|
exports.parseStrict = parseStrict;
|
|
122
122
|
function findEnvfiles(functionsSource, projectId, projectAlias, isEmulator) {
|
|
123
123
|
const files = [".env"];
|
|
124
|
+
files.push(`.env.${projectId}`);
|
|
125
|
+
if (projectAlias) {
|
|
126
|
+
files.push(`.env.${projectAlias}`);
|
|
127
|
+
}
|
|
124
128
|
if (isEmulator) {
|
|
125
129
|
files.push(FUNCTIONS_EMULATOR_DOTENV);
|
|
126
130
|
}
|
|
127
|
-
else {
|
|
128
|
-
files.push(`.env.${projectId}`);
|
|
129
|
-
if (projectAlias && projectAlias.length) {
|
|
130
|
-
files.push(`.env.${projectAlias}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
131
|
return files
|
|
134
132
|
.map((f) => path.join(functionsSource, f))
|
|
135
133
|
.filter(fs.existsSync)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
|
|
4
|
+
exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
|
|
5
|
+
exports.STORAGE_EVENTS = [
|
|
6
|
+
"google.cloud.storage.object.v1.finalized",
|
|
7
|
+
"google.cloud.storage.object.v1.archived",
|
|
8
|
+
"google.cloud.storage.object.v1.deleted",
|
|
9
|
+
"google.cloud.storage.object.v1.metadataUpdated",
|
|
10
|
+
];
|
|
11
|
+
exports.FIREBASE_ALERTS_PUBLISH_EVENT = "google.firebase.firebasealerts.alerts.v1.published";
|
|
@@ -202,7 +202,7 @@ async function listAllFunctions(projectId) {
|
|
|
202
202
|
}
|
|
203
203
|
exports.listAllFunctions = listAllFunctions;
|
|
204
204
|
function endpointFromFunction(gcfFunction) {
|
|
205
|
-
var _a, _b, _c;
|
|
205
|
+
var _a, _b, _c, _d;
|
|
206
206
|
const [, project, , region, , id] = gcfFunction.name.split("/");
|
|
207
207
|
let trigger;
|
|
208
208
|
let uri;
|
|
@@ -217,6 +217,11 @@ function endpointFromFunction(gcfFunction) {
|
|
|
217
217
|
taskQueueTrigger: {},
|
|
218
218
|
};
|
|
219
219
|
}
|
|
220
|
+
else if ((_c = gcfFunction.labels) === null || _c === void 0 ? void 0 : _c["deployment-callable"]) {
|
|
221
|
+
trigger = {
|
|
222
|
+
callableTrigger: {},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
220
225
|
else if (gcfFunction.httpsTrigger) {
|
|
221
226
|
trigger = { httpsTrigger: {} };
|
|
222
227
|
uri = gcfFunction.httpsTrigger.url;
|
|
@@ -226,10 +231,13 @@ function endpointFromFunction(gcfFunction) {
|
|
|
226
231
|
trigger = {
|
|
227
232
|
eventTrigger: {
|
|
228
233
|
eventType: gcfFunction.eventTrigger.eventType,
|
|
229
|
-
eventFilters:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
234
|
+
eventFilters: [
|
|
235
|
+
{
|
|
236
|
+
attribute: "resource",
|
|
237
|
+
value: gcfFunction.eventTrigger.resource,
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
retry: !!((_d = gcfFunction.eventTrigger.failurePolicy) === null || _d === void 0 ? void 0 : _d.retry),
|
|
233
241
|
},
|
|
234
242
|
};
|
|
235
243
|
}
|
|
@@ -269,9 +277,13 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
|
|
|
269
277
|
};
|
|
270
278
|
proto.copyIfPresent(gcfFunction, endpoint, "labels");
|
|
271
279
|
if (backend.isEventTriggered(endpoint)) {
|
|
280
|
+
const resourceFilter = backend.findEventFilter(endpoint, "resource");
|
|
281
|
+
if (!resourceFilter) {
|
|
282
|
+
throw new error_1.FirebaseError("Invalid event trigger definition. Expected event filter with 'resource' attribute.");
|
|
283
|
+
}
|
|
272
284
|
gcfFunction.eventTrigger = {
|
|
273
285
|
eventType: endpoint.eventTrigger.eventType,
|
|
274
|
-
resource:
|
|
286
|
+
resource: resourceFilter.value,
|
|
275
287
|
};
|
|
276
288
|
gcfFunction.eventTrigger.failurePolicy = endpoint.eventTrigger.retry
|
|
277
289
|
? { retry: {} }
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.megabytes = exports.
|
|
3
|
+
exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.megabytes = exports.API_VERSION = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const apiv2_1 = require("../apiv2");
|
|
6
6
|
const error_1 = require("../error");
|
|
7
7
|
const api_1 = require("../api");
|
|
8
8
|
const logger_1 = require("../logger");
|
|
9
|
+
const v2_1 = require("../functions/events/v2");
|
|
9
10
|
const backend = require("../deploy/functions/backend");
|
|
10
11
|
const runtimes = require("../deploy/functions/runtimes");
|
|
11
12
|
const proto = require("./proto");
|
|
@@ -16,7 +17,6 @@ const client = new apiv2_1.Client({
|
|
|
16
17
|
auth: true,
|
|
17
18
|
apiVersion: exports.API_VERSION,
|
|
18
19
|
});
|
|
19
|
-
exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
|
|
20
20
|
const BYTES_PER_UNIT = {
|
|
21
21
|
"": 1,
|
|
22
22
|
k: 1e3,
|
|
@@ -179,14 +179,24 @@ function functionFromEndpoint(endpoint, source) {
|
|
|
179
179
|
gcfFunction.eventTrigger = {
|
|
180
180
|
eventType: endpoint.eventTrigger.eventType,
|
|
181
181
|
};
|
|
182
|
-
if (gcfFunction.eventTrigger.eventType ===
|
|
183
|
-
|
|
182
|
+
if (gcfFunction.eventTrigger.eventType === v2_1.PUBSUB_PUBLISH_EVENT) {
|
|
183
|
+
const pubsubFilter = backend.findEventFilter(endpoint, "topic");
|
|
184
|
+
if (!pubsubFilter) {
|
|
185
|
+
throw new error_1.FirebaseError("Invalid pubsub endpoint. Expected eventFilter with 'topic' attribute but found none.");
|
|
186
|
+
}
|
|
187
|
+
gcfFunction.eventTrigger.pubsubTopic = pubsubFilter.value;
|
|
188
|
+
for (const filter of endpoint.eventTrigger.eventFilters) {
|
|
189
|
+
if (filter.attribute === "topic") {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (!gcfFunction.eventTrigger.eventFilters) {
|
|
193
|
+
gcfFunction.eventTrigger.eventFilters = [];
|
|
194
|
+
}
|
|
195
|
+
gcfFunction.eventTrigger.eventFilters.push(filter);
|
|
196
|
+
}
|
|
184
197
|
}
|
|
185
198
|
else {
|
|
186
|
-
gcfFunction.eventTrigger.eventFilters =
|
|
187
|
-
for (const [attribute, value] of Object.entries(endpoint.eventTrigger.eventFilters)) {
|
|
188
|
-
gcfFunction.eventTrigger.eventFilters.push({ attribute, value });
|
|
189
|
-
}
|
|
199
|
+
gcfFunction.eventTrigger.eventFilters = endpoint.eventTrigger.eventFilters;
|
|
190
200
|
}
|
|
191
201
|
proto.renameIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "triggerRegion", "region");
|
|
192
202
|
if (endpoint.eventTrigger.retry) {
|
|
@@ -207,7 +217,7 @@ function functionFromEndpoint(endpoint, source) {
|
|
|
207
217
|
}
|
|
208
218
|
exports.functionFromEndpoint = functionFromEndpoint;
|
|
209
219
|
function endpointFromFunction(gcfFunction) {
|
|
210
|
-
var _a, _b;
|
|
220
|
+
var _a, _b, _c;
|
|
211
221
|
const [, project, , region, , id] = gcfFunction.name.split("/");
|
|
212
222
|
let trigger;
|
|
213
223
|
if (((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true") {
|
|
@@ -220,20 +230,28 @@ function endpointFromFunction(gcfFunction) {
|
|
|
220
230
|
taskQueueTrigger: {},
|
|
221
231
|
};
|
|
222
232
|
}
|
|
233
|
+
else if (((_c = gcfFunction.labels) === null || _c === void 0 ? void 0 : _c["deployment-callable"]) === "true") {
|
|
234
|
+
trigger = {
|
|
235
|
+
callableTrigger: {},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
223
238
|
else if (gcfFunction.eventTrigger) {
|
|
224
239
|
trigger = {
|
|
225
240
|
eventTrigger: {
|
|
226
241
|
eventType: gcfFunction.eventTrigger.eventType,
|
|
227
|
-
eventFilters:
|
|
242
|
+
eventFilters: [],
|
|
228
243
|
retry: false,
|
|
229
244
|
},
|
|
230
245
|
};
|
|
231
246
|
if (gcfFunction.eventTrigger.pubsubTopic) {
|
|
232
|
-
trigger.eventTrigger.eventFilters.
|
|
247
|
+
trigger.eventTrigger.eventFilters.push({
|
|
248
|
+
attribute: "topic",
|
|
249
|
+
value: gcfFunction.eventTrigger.pubsubTopic,
|
|
250
|
+
});
|
|
233
251
|
}
|
|
234
252
|
else {
|
|
235
253
|
for (const { attribute, value } of gcfFunction.eventTrigger.eventFilters || []) {
|
|
236
|
-
trigger.eventTrigger.eventFilters
|
|
254
|
+
trigger.eventTrigger.eventFilters.push({ attribute, value });
|
|
237
255
|
}
|
|
238
256
|
}
|
|
239
257
|
proto.renameIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "region", "triggerRegion");
|
|
@@ -13,13 +13,13 @@ exports.firebaseRoles = {
|
|
|
13
13
|
hostingAdmin: "roles/firebasehosting.admin",
|
|
14
14
|
runViewer: "roles/run.viewer",
|
|
15
15
|
};
|
|
16
|
-
async function getIamPolicy(
|
|
17
|
-
const response = await apiClient.post(`/projects/${
|
|
16
|
+
async function getIamPolicy(projectIdOrNumber) {
|
|
17
|
+
const response = await apiClient.post(`/projects/${projectIdOrNumber}:getIamPolicy`);
|
|
18
18
|
return response.body;
|
|
19
19
|
}
|
|
20
20
|
exports.getIamPolicy = getIamPolicy;
|
|
21
|
-
async function setIamPolicy(
|
|
22
|
-
const response = await apiClient.post(`/projects/${
|
|
21
|
+
async function setIamPolicy(projectIdOrNumber, newPolicy, updateMask = "") {
|
|
22
|
+
const response = await apiClient.post(`/projects/${projectIdOrNumber}:setIamPolicy`, {
|
|
23
23
|
policy: newPolicy,
|
|
24
24
|
updateMask: updateMask,
|
|
25
25
|
});
|
package/lib/serve/functions.js
CHANGED
|
@@ -29,8 +29,9 @@ class FunctionsServer {
|
|
|
29
29
|
functionsDir,
|
|
30
30
|
nodeMajorVersion,
|
|
31
31
|
env: {},
|
|
32
|
+
secretEnv: [],
|
|
32
33
|
};
|
|
33
|
-
const args = Object.assign({ projectId, projectDir: options.config.projectDir, emulatableBackends: [this.backend], account }, partialArgs);
|
|
34
|
+
const args = Object.assign({ projectId, projectDir: options.config.projectDir, emulatableBackends: [this.backend], projectAlias: options.projectAlias, account }, partialArgs);
|
|
34
35
|
if (options.host) {
|
|
35
36
|
utils.assertIsStringOrUndefined(options.host);
|
|
36
37
|
args.host = options.host;
|