firebase-tools 10.2.2 → 10.4.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/deploy.js +1 -1
- package/lib/commands/experimental-functions-shell.js +1 -1
- package/lib/commands/ext-configure.js +68 -7
- package/lib/commands/ext-export.js +10 -9
- package/lib/commands/ext-install.js +73 -9
- package/lib/commands/ext-uninstall.js +9 -0
- package/lib/commands/ext-update.js +58 -3
- package/lib/commands/functions-config-export.js +2 -2
- package/lib/commands/functions-shell.js +1 -1
- package/lib/commands/hosting-channel-create.js +2 -2
- package/lib/commands/hosting-channel-delete.js +2 -2
- package/lib/commands/hosting-channel-deploy.js +2 -2
- package/lib/commands/hosting-channel-list.js +2 -2
- package/lib/commands/hosting-channel-open.js +2 -2
- package/lib/commands/hosting-sites-delete.js +2 -2
- package/lib/commands/serve.js +1 -1
- package/lib/commands/target-apply.js +2 -2
- package/lib/commands/target-clear.js +2 -2
- package/lib/commands/target-remove.js +2 -2
- package/lib/commands/target.js +2 -2
- package/lib/config.js +9 -3
- package/lib/deploy/extensions/planner.js +15 -9
- 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 +36 -13
- 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/auth/apiSpec.js +37 -0
- package/lib/emulator/commandUtils.js +2 -2
- 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 +47 -9
- package/lib/emulator/functionsEmulator.js +17 -12
- package/lib/emulator/functionsEmulatorShared.js +34 -11
- package/lib/emulator/storage/apis/firebase.js +316 -341
- 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 +25 -74
- 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 +34 -0
- package/lib/emulator/storage/rules/manager.js +98 -0
- package/lib/emulator/storage/rules/runtime.js +4 -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/askUserForParam.js +77 -28
- package/lib/extensions/emulator/optionsHelper.js +35 -3
- package/lib/extensions/extensionsHelper.js +19 -10
- package/lib/extensions/manifest.js +142 -14
- package/lib/extensions/paramHelper.js +32 -9
- package/lib/fsutils.js +14 -1
- package/lib/functions/env.js +4 -6
- package/lib/functions/events/v2.js +11 -0
- package/lib/gcp/cloudfunctions.js +20 -7
- package/lib/gcp/cloudfunctionsv2.js +30 -12
- package/lib/gcp/resourceManager.js +4 -4
- package/lib/requireConfig.js +11 -9
- 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
- package/lib/prepareUpload.js +0 -44
|
@@ -5705,6 +5705,28 @@ exports.default = {
|
|
|
5705
5705
|
},
|
|
5706
5706
|
type: "object",
|
|
5707
5707
|
},
|
|
5708
|
+
GoogleCloudIdentitytoolkitAdminV2AllowByDefault: {
|
|
5709
|
+
description: "Defines a policy of allowing every region by default and adding disallowed regions to a disallow list.",
|
|
5710
|
+
properties: {
|
|
5711
|
+
disallowedRegions: {
|
|
5712
|
+
description: "Two letter unicode region codes to disallow as defined by https://cldr.unicode.org/ The full list of these region codes is here: https://github.com/unicode-cldr/cldr-localenames-full/blob/master/main/en/territories.json",
|
|
5713
|
+
items: { type: "string" },
|
|
5714
|
+
type: "array",
|
|
5715
|
+
},
|
|
5716
|
+
},
|
|
5717
|
+
type: "object",
|
|
5718
|
+
},
|
|
5719
|
+
GoogleCloudIdentitytoolkitAdminV2AllowlistOnly: {
|
|
5720
|
+
description: "Defines a policy of only allowing regions by explicitly adding them to an allowlist.",
|
|
5721
|
+
properties: {
|
|
5722
|
+
allowedRegions: {
|
|
5723
|
+
description: "Two letter unicode region codes to allow as defined by https://cldr.unicode.org/ The full list of these region codes is here: https://github.com/unicode-cldr/cldr-localenames-full/blob/master/main/en/territories.json",
|
|
5724
|
+
items: { type: "string" },
|
|
5725
|
+
type: "array",
|
|
5726
|
+
},
|
|
5727
|
+
},
|
|
5728
|
+
type: "object",
|
|
5729
|
+
},
|
|
5708
5730
|
GoogleCloudIdentitytoolkitAdminV2Anonymous: {
|
|
5709
5731
|
description: "Configuration options related to authenticating an anonymous user.",
|
|
5710
5732
|
properties: {
|
|
@@ -6315,6 +6337,18 @@ exports.default = {
|
|
|
6315
6337
|
},
|
|
6316
6338
|
type: "object",
|
|
6317
6339
|
},
|
|
6340
|
+
GoogleCloudIdentitytoolkitAdminV2SmsRegionConfig: {
|
|
6341
|
+
description: "Configures the regions where users are allowed to send verification SMS for the project or tenant. This is based on the calling code of the destination phone number.",
|
|
6342
|
+
properties: {
|
|
6343
|
+
allowByDefault: {
|
|
6344
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2AllowByDefault",
|
|
6345
|
+
},
|
|
6346
|
+
allowlistOnly: {
|
|
6347
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2AllowlistOnly",
|
|
6348
|
+
},
|
|
6349
|
+
},
|
|
6350
|
+
type: "object",
|
|
6351
|
+
},
|
|
6318
6352
|
GoogleCloudIdentitytoolkitAdminV2SmsTemplate: {
|
|
6319
6353
|
description: "The template to use when sending an SMS.",
|
|
6320
6354
|
properties: {
|
|
@@ -6424,6 +6458,9 @@ exports.default = {
|
|
|
6424
6458
|
readOnly: true,
|
|
6425
6459
|
type: "string",
|
|
6426
6460
|
},
|
|
6461
|
+
smsRegionConfig: {
|
|
6462
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2SmsRegionConfig",
|
|
6463
|
+
},
|
|
6427
6464
|
testPhoneNumbers: {
|
|
6428
6465
|
additionalProperties: { type: "string" },
|
|
6429
6466
|
description: "A map of pairs that can be used for MFA. The phone number should be in E.164 format (https://www.itu.int/rec/T-REC-E.164/) and a maximum of 10 pairs can be added (error will be thrown once exceeded).",
|
|
@@ -10,7 +10,7 @@ const logger_1 = require("../logger");
|
|
|
10
10
|
const path = require("path");
|
|
11
11
|
const constants_1 = require("./constants");
|
|
12
12
|
const requireAuth_1 = require("../requireAuth");
|
|
13
|
-
const
|
|
13
|
+
const requireConfig_1 = require("../requireConfig");
|
|
14
14
|
const types_1 = require("./types");
|
|
15
15
|
const error_1 = require("../error");
|
|
16
16
|
const registry_1 = require("./registry");
|
|
@@ -102,7 +102,7 @@ async function beforeEmulatorCommand(options) {
|
|
|
102
102
|
options.config = DEFAULT_CONFIG;
|
|
103
103
|
}
|
|
104
104
|
else {
|
|
105
|
-
await requireConfig(options);
|
|
105
|
+
await (0, requireConfig_1.requireConfig)(options);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
exports.beforeEmulatorCommand = beforeEmulatorCommand;
|
|
@@ -90,6 +90,7 @@ class Constants {
|
|
|
90
90
|
}
|
|
91
91
|
exports.Constants = Constants;
|
|
92
92
|
Constants.FAKE_PROJECT_ID_PREFIX = "demo-";
|
|
93
|
+
Constants.FAKE_PROJECT_NUMBER = "0";
|
|
93
94
|
Constants.DEFAULT_DATABASE_EMULATOR_NAMESPACE = "fake-server";
|
|
94
95
|
Constants.FIRESTORE_EMULATOR_HOST = "FIRESTORE_EMULATOR_HOST";
|
|
95
96
|
Constants.FIREBASE_DATABASE_EMULATOR_HOST = "FIREBASE_DATABASE_EMULATOR_HOST";
|
|
@@ -33,6 +33,7 @@ const prompt_1 = require("../prompt");
|
|
|
33
33
|
const commandUtils_1 = require("./commandUtils");
|
|
34
34
|
const fsutils_1 = require("../fsutils");
|
|
35
35
|
const storage_1 = require("./storage");
|
|
36
|
+
const config_1 = require("./storage/rules/config");
|
|
36
37
|
const getDefaultDatabaseInstance_1 = require("../getDefaultDatabaseInstance");
|
|
37
38
|
const auth_2 = require("../auth");
|
|
38
39
|
const extensionsEmulator_1 = require("./extensionsEmulator");
|
|
@@ -259,12 +260,15 @@ async function startAll(options, showUI = true) {
|
|
|
259
260
|
emulatableBackends.push({
|
|
260
261
|
functionsDir,
|
|
261
262
|
env: Object.assign({}, options.extDevEnv),
|
|
263
|
+
secretEnv: [],
|
|
262
264
|
predefinedTriggers: options.extDevTriggers,
|
|
263
265
|
nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion || options.config.get("functions.runtime")),
|
|
264
266
|
});
|
|
265
267
|
}
|
|
266
268
|
if (shouldStart(options, types_1.Emulators.EXTENSIONS) && previews_1.previews.extensionsemulator) {
|
|
267
|
-
const projectNumber =
|
|
269
|
+
const projectNumber = constants_1.Constants.isDemoProject(projectId)
|
|
270
|
+
? constants_1.Constants.FAKE_PROJECT_NUMBER
|
|
271
|
+
: await (0, projectUtils_1.needProjectNumber)(options);
|
|
268
272
|
const aliases = (0, projectUtils_1.getAliases)(options, projectId);
|
|
269
273
|
const extensionEmulator = new extensionsEmulator_1.ExtensionsEmulator({
|
|
270
274
|
projectId,
|
|
@@ -274,7 +278,8 @@ async function startAll(options, showUI = true) {
|
|
|
274
278
|
extensions: options.config.get("extensions"),
|
|
275
279
|
});
|
|
276
280
|
const extensionsBackends = await extensionEmulator.getExtensionBackends();
|
|
277
|
-
|
|
281
|
+
const filteredExtensionsBackends = extensionEmulator.filterUnemulatedTriggers(options, extensionsBackends);
|
|
282
|
+
emulatableBackends.push(...filteredExtensionsBackends);
|
|
278
283
|
void track("Emulator Run", types_1.Emulators.EXTENSIONS);
|
|
279
284
|
registry_1.EmulatorRegistry.registerExtensionsEmulator();
|
|
280
285
|
}
|
|
@@ -302,6 +307,7 @@ async function startAll(options, showUI = true) {
|
|
|
302
307
|
host: functionsAddr.host,
|
|
303
308
|
port: functionsAddr.port,
|
|
304
309
|
debugPort: inspectFunctions,
|
|
310
|
+
projectAlias: options.projectAlias,
|
|
305
311
|
});
|
|
306
312
|
await startEmulator(functionsEmulator);
|
|
307
313
|
}
|
|
@@ -421,15 +427,11 @@ async function startAll(options, showUI = true) {
|
|
|
421
427
|
}
|
|
422
428
|
if (shouldStart(options, types_1.Emulators.STORAGE)) {
|
|
423
429
|
const storageAddr = await getAndCheckAddress(types_1.Emulators.STORAGE, options);
|
|
424
|
-
const storageConfig = options.config.data.storage;
|
|
425
|
-
if (!(storageConfig === null || storageConfig === void 0 ? void 0 : storageConfig.rules)) {
|
|
426
|
-
throw new error_1.FirebaseError("Cannot start the Storage emulator without rules file specified in firebase.json: run 'firebase init' and set up your Storage configuration");
|
|
427
|
-
}
|
|
428
430
|
const storageEmulator = new storage_1.StorageEmulator({
|
|
429
431
|
host: storageAddr.host,
|
|
430
432
|
port: storageAddr.port,
|
|
431
433
|
projectId: projectId,
|
|
432
|
-
rules:
|
|
434
|
+
rules: (0, config_1.getStorageRulesConfig)(projectId, options),
|
|
433
435
|
});
|
|
434
436
|
await startEmulator(storageEmulator);
|
|
435
437
|
if (exportMetadata.storage) {
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getUnemulatedAPIs = void 0;
|
|
3
|
+
exports.checkForUnemulatedTriggerTypes = exports.getUnemulatedAPIs = void 0;
|
|
4
4
|
const planner = require("../../deploy/extensions/planner");
|
|
5
|
+
const controller_1 = require("../controller");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
5
7
|
const ensureApiEnabled_1 = require("../../ensureApiEnabled");
|
|
8
|
+
const functionsEmulatorShared_1 = require("../functionsEmulatorShared");
|
|
9
|
+
const types_1 = require("../types");
|
|
6
10
|
const EMULATED_APIS = [
|
|
7
11
|
"storage-component.googleapis.com",
|
|
8
12
|
"firestore.googleapis.com",
|
|
@@ -20,7 +24,8 @@ async function getUnemulatedAPIs(projectId, instances) {
|
|
|
20
24
|
unemulatedAPIs[api.apiName].instanceIds.push(i.instanceId);
|
|
21
25
|
}
|
|
22
26
|
else {
|
|
23
|
-
const enabled =
|
|
27
|
+
const enabled = !constants_1.Constants.isDemoProject(projectId) &&
|
|
28
|
+
(await (0, ensureApiEnabled_1.check)(projectId, api.apiName, "extensions", true));
|
|
24
29
|
unemulatedAPIs[api.apiName] = {
|
|
25
30
|
apiName: api.apiName,
|
|
26
31
|
instanceIds: [i.instanceId],
|
|
@@ -33,3 +38,33 @@ async function getUnemulatedAPIs(projectId, instances) {
|
|
|
33
38
|
return Object.values(unemulatedAPIs);
|
|
34
39
|
}
|
|
35
40
|
exports.getUnemulatedAPIs = getUnemulatedAPIs;
|
|
41
|
+
function checkForUnemulatedTriggerTypes(backend, options) {
|
|
42
|
+
var _a;
|
|
43
|
+
const triggers = (_a = backend.predefinedTriggers) !== null && _a !== void 0 ? _a : [];
|
|
44
|
+
const unemulatedTriggers = triggers
|
|
45
|
+
.filter((definition) => {
|
|
46
|
+
if (definition.httpsTrigger) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (definition.eventTrigger) {
|
|
50
|
+
const service = (0, functionsEmulatorShared_1.getFunctionService)(definition);
|
|
51
|
+
switch (service) {
|
|
52
|
+
case constants_1.Constants.SERVICE_FIRESTORE:
|
|
53
|
+
return !(0, controller_1.shouldStart)(options, types_1.Emulators.FIRESTORE);
|
|
54
|
+
case constants_1.Constants.SERVICE_REALTIME_DATABASE:
|
|
55
|
+
return !(0, controller_1.shouldStart)(options, types_1.Emulators.DATABASE);
|
|
56
|
+
case constants_1.Constants.SERVICE_PUBSUB:
|
|
57
|
+
return !(0, controller_1.shouldStart)(options, types_1.Emulators.PUBSUB);
|
|
58
|
+
case constants_1.Constants.SERVICE_AUTH:
|
|
59
|
+
return !(0, controller_1.shouldStart)(options, types_1.Emulators.AUTH);
|
|
60
|
+
case constants_1.Constants.SERVICE_STORAGE:
|
|
61
|
+
return !(0, controller_1.shouldStart)(options, types_1.Emulators.STORAGE);
|
|
62
|
+
default:
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.map((definition) => constants_1.Constants.getServiceName((0, functionsEmulatorShared_1.getFunctionService)(definition)));
|
|
68
|
+
return [...new Set(unemulatedTriggers)];
|
|
69
|
+
}
|
|
70
|
+
exports.checkForUnemulatedTriggerTypes = checkForUnemulatedTriggerTypes;
|
|
@@ -17,10 +17,12 @@ const types_1 = require("./types");
|
|
|
17
17
|
const validation_1 = require("./extensions/validation");
|
|
18
18
|
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
19
19
|
const shortenUrl_1 = require("../shortenUrl");
|
|
20
|
+
const constants_1 = require("./constants");
|
|
20
21
|
class ExtensionsEmulator {
|
|
21
22
|
constructor(args) {
|
|
22
23
|
this.want = [];
|
|
23
24
|
this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.EXTENSIONS);
|
|
25
|
+
this.pendingDownloads = new Map();
|
|
24
26
|
this.args = args;
|
|
25
27
|
}
|
|
26
28
|
async readManifest() {
|
|
@@ -31,7 +33,7 @@ class ExtensionsEmulator {
|
|
|
31
33
|
aliases: (_a = this.args.aliases) !== null && _a !== void 0 ? _a : [],
|
|
32
34
|
projectDir: this.args.projectDir,
|
|
33
35
|
extensions: this.args.extensions,
|
|
34
|
-
|
|
36
|
+
emulatorMode: true,
|
|
35
37
|
});
|
|
36
38
|
}
|
|
37
39
|
async ensureSourceCode(instance) {
|
|
@@ -42,19 +44,30 @@ class ExtensionsEmulator {
|
|
|
42
44
|
const cacheDir = process.env.FIREBASE_EXTENSIONS_CACHE_PATH ||
|
|
43
45
|
path.join(os.homedir(), ".cache", "firebase", "extensions");
|
|
44
46
|
const sourceCodePath = path.join(cacheDir, ref);
|
|
47
|
+
if (this.pendingDownloads.get(ref)) {
|
|
48
|
+
await this.pendingDownloads.get(ref);
|
|
49
|
+
}
|
|
45
50
|
if (!this.hasValidSource({ path: sourceCodePath, extRef: ref })) {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
const promise = this.downloadSource(instance, ref, sourceCodePath);
|
|
52
|
+
this.pendingDownloads.set(ref, promise);
|
|
53
|
+
await promise;
|
|
49
54
|
}
|
|
50
55
|
return sourceCodePath;
|
|
51
56
|
}
|
|
57
|
+
async downloadSource(instance, ref, sourceCodePath) {
|
|
58
|
+
const extensionVersion = await planner.getExtensionVersion(instance);
|
|
59
|
+
await (0, download_1.downloadExtensionVersion)(ref, extensionVersion.sourceDownloadUri, sourceCodePath);
|
|
60
|
+
this.installAndBuildSourceCode(sourceCodePath);
|
|
61
|
+
}
|
|
52
62
|
hasValidSource(args) {
|
|
53
63
|
const requiredFiles = [
|
|
54
64
|
"./extension.yaml",
|
|
55
65
|
"./functions/package.json",
|
|
56
66
|
"./functions/node_modules",
|
|
57
67
|
];
|
|
68
|
+
if (!fs.existsSync(args.path)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
58
71
|
for (const requiredFile of requiredFiles) {
|
|
59
72
|
const f = path.join(args.path, requiredFile);
|
|
60
73
|
if (!fs.existsSync(f)) {
|
|
@@ -87,12 +100,13 @@ class ExtensionsEmulator {
|
|
|
87
100
|
const extensionDir = await this.ensureSourceCode(instance);
|
|
88
101
|
const functionsDir = path.join(extensionDir, "functions");
|
|
89
102
|
const env = Object.assign(this.autoPopulatedParams(instance), instance.params);
|
|
90
|
-
const { extensionTriggers, nodeMajorVersion } = await (0, optionsHelper_1.getExtensionFunctionInfo)(extensionDir, instance.instanceId, env);
|
|
103
|
+
const { extensionTriggers, nodeMajorVersion, nonSecretEnv, secretEnvVariables } = await (0, optionsHelper_1.getExtensionFunctionInfo)(extensionDir, instance.instanceId, env);
|
|
91
104
|
const extension = await planner.getExtension(instance);
|
|
92
105
|
const extensionVersion = await planner.getExtensionVersion(instance);
|
|
93
106
|
return {
|
|
94
107
|
functionsDir,
|
|
95
|
-
env,
|
|
108
|
+
env: nonSecretEnv,
|
|
109
|
+
secretEnv: secretEnvVariables,
|
|
96
110
|
predefinedTriggers: extensionTriggers,
|
|
97
111
|
nodeMajorVersion: nodeMajorVersion,
|
|
98
112
|
extensionInstanceId: instance.instanceId,
|
|
@@ -131,10 +145,34 @@ class ExtensionsEmulator {
|
|
|
131
145
|
apiToWarn.enabled ? "" : clc.bold.underline(enablementUri),
|
|
132
146
|
]);
|
|
133
147
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
148
|
+
if (constants_1.Constants.isDemoProject(this.args.projectId)) {
|
|
149
|
+
this.logger.logLabeled("WARN", "Extensions", "The following Extensions make calls to Google Cloud APIs that do not have Emulators. " +
|
|
150
|
+
`${clc.bold(this.args.projectId)} is a demo project, so these Extensions may not work as expected.\n` +
|
|
151
|
+
table.toString());
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
this.logger.logLabeled("WARN", "Extensions", "The following Extensions make calls to Google Cloud APIs that do not have Emulators. " +
|
|
155
|
+
`These calls will go to production Google Cloud APIs which may have real effects on ${clc.bold(this.args.projectId)}.\n` +
|
|
156
|
+
table.toString());
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
filterUnemulatedTriggers(options, backends) {
|
|
161
|
+
let foundUnemulatedTrigger = false;
|
|
162
|
+
const filteredBackends = backends.filter((backend) => {
|
|
163
|
+
const unemulatedServices = (0, validation_1.checkForUnemulatedTriggerTypes)(backend, options);
|
|
164
|
+
if (unemulatedServices.length) {
|
|
165
|
+
foundUnemulatedTrigger = true;
|
|
166
|
+
const msg = ` ignored becuase it includes ${unemulatedServices.join(", ")} triggered functions, and the ${unemulatedServices.join(", ")} emulator does not exist or is not running.`;
|
|
167
|
+
this.logger.logLabeled("WARN", `extensions[${backend.extensionInstanceId}]`, msg);
|
|
168
|
+
}
|
|
169
|
+
return unemulatedServices.length === 0;
|
|
170
|
+
});
|
|
171
|
+
if (foundUnemulatedTrigger) {
|
|
172
|
+
const msg = "No Cloud Functions for these instances will be emulated, because partially emulating an Extension can lead to unexpected behavior. ";
|
|
173
|
+
this.logger.log("WARN", msg);
|
|
137
174
|
}
|
|
175
|
+
return filteredBackends;
|
|
138
176
|
}
|
|
139
177
|
}
|
|
140
178
|
exports.ExtensionsEmulator = ExtensionsEmulator;
|
|
@@ -35,7 +35,6 @@ const runtimes = require("../deploy/functions/runtimes");
|
|
|
35
35
|
const backend = require("../deploy/functions/backend");
|
|
36
36
|
const functionsEnv = require("../functions/env");
|
|
37
37
|
const EVENT_INVOKE = "functions:invoke";
|
|
38
|
-
const LOCAL_SECRETS_FILE = ".secret.local";
|
|
39
38
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
40
39
|
class FunctionsEmulator {
|
|
41
40
|
constructor(args) {
|
|
@@ -79,7 +78,6 @@ class FunctionsEmulator {
|
|
|
79
78
|
createHubServer() {
|
|
80
79
|
this.workQueue.start();
|
|
81
80
|
const hub = express();
|
|
82
|
-
hub.use(cors({ origin: true }));
|
|
83
81
|
const dataMiddleware = (req, res, next) => {
|
|
84
82
|
const chunks = [];
|
|
85
83
|
req.on("data", (chunk) => {
|
|
@@ -158,7 +156,7 @@ class FunctionsEmulator {
|
|
|
158
156
|
const listBackendsHandler = (req, res) => {
|
|
159
157
|
res.json({ backends: this.getBackendInfo() });
|
|
160
158
|
};
|
|
161
|
-
hub.get(listBackendsRoute,
|
|
159
|
+
hub.get(listBackendsRoute, cors({ origin: true }), listBackendsHandler);
|
|
162
160
|
hub.post(backgroundFunctionRoute, dataMiddleware, backgroundHandler);
|
|
163
161
|
hub.post(multicastFunctionRoute, dataMiddleware, multicastHandler);
|
|
164
162
|
hub.all(httpsFunctionRoutes, dataMiddleware, httpsHandler);
|
|
@@ -253,7 +251,7 @@ class FunctionsEmulator {
|
|
|
253
251
|
}
|
|
254
252
|
let triggerDefinitions;
|
|
255
253
|
if (emulatableBackend.predefinedTriggers) {
|
|
256
|
-
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers);
|
|
254
|
+
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers, emulatableBackend.secretEnv);
|
|
257
255
|
}
|
|
258
256
|
else {
|
|
259
257
|
const runtimeConfig = this.getRuntimeConfig(emulatableBackend);
|
|
@@ -495,9 +493,13 @@ class FunctionsEmulator {
|
|
|
495
493
|
.map((t) => t.def);
|
|
496
494
|
return this.args.emulatableBackends.map((e) => {
|
|
497
495
|
var _a;
|
|
496
|
+
const envWithSecrets = Object.assign({}, e.env);
|
|
497
|
+
for (const s of e.secretEnv) {
|
|
498
|
+
envWithSecrets[s.key] = backend.secretVersionName(s);
|
|
499
|
+
}
|
|
498
500
|
return {
|
|
499
501
|
directory: e.functionsDir,
|
|
500
|
-
env:
|
|
502
|
+
env: envWithSecrets,
|
|
501
503
|
extensionInstanceId: e.extensionInstanceId,
|
|
502
504
|
extension: e.extension,
|
|
503
505
|
extensionVersion: e.extensionVersion,
|
|
@@ -570,6 +572,7 @@ class FunctionsEmulator {
|
|
|
570
572
|
const projectInfo = {
|
|
571
573
|
functionsSource: backend.functionsDir,
|
|
572
574
|
projectId: this.args.projectId,
|
|
575
|
+
projectAlias: this.args.projectAlias,
|
|
573
576
|
isEmulator: true,
|
|
574
577
|
};
|
|
575
578
|
if (functionsEnv.hasUserEnvs(projectInfo)) {
|
|
@@ -650,23 +653,25 @@ class FunctionsEmulator {
|
|
|
650
653
|
}
|
|
651
654
|
async resolveSecretEnvs(backend, trigger) {
|
|
652
655
|
let secretEnvs = {};
|
|
656
|
+
const secretPath = (0, functionsEmulatorShared_1.getSecretLocalPath)(backend, this.args.projectDir);
|
|
653
657
|
try {
|
|
654
|
-
const data = fs.readFileSync(
|
|
658
|
+
const data = fs.readFileSync(secretPath, "utf8");
|
|
655
659
|
secretEnvs = functionsEnv.parseStrict(data);
|
|
656
660
|
}
|
|
657
661
|
catch (e) {
|
|
658
662
|
if (e.code !== "ENOENT") {
|
|
659
|
-
this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${
|
|
663
|
+
this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${secretPath}: ${e.message}`);
|
|
660
664
|
}
|
|
661
665
|
}
|
|
662
666
|
if (trigger) {
|
|
663
667
|
const secrets = trigger.secretEnvironmentVariables || [];
|
|
664
668
|
const accesses = secrets
|
|
665
|
-
.filter((s) => !secretEnvs[s.
|
|
669
|
+
.filter((s) => !secretEnvs[s.key])
|
|
666
670
|
.map(async (s) => {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
671
|
+
var _a;
|
|
672
|
+
this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.secret}@latest`);
|
|
673
|
+
const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.secret, (_a = s.version) !== null && _a !== void 0 ? _a : "latest");
|
|
674
|
+
return [s.key, value];
|
|
670
675
|
});
|
|
671
676
|
const accessResults = await (0, utils_1.allSettled)(accesses);
|
|
672
677
|
const errs = [];
|
|
@@ -682,7 +687,7 @@ class FunctionsEmulator {
|
|
|
682
687
|
if (errs.length > 0) {
|
|
683
688
|
this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
|
|
684
689
|
"Make sure the credential used for the Functions Emulator have access " +
|
|
685
|
-
`or provide override values in ${
|
|
690
|
+
`or provide override values in ${secretPath}:\n\t` +
|
|
686
691
|
errs.join("\n\t"));
|
|
687
692
|
}
|
|
688
693
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
3
|
+
exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const os = require("os");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const fs = require("fs");
|
|
8
|
+
const backend = require("../deploy/functions/backend");
|
|
8
9
|
const constants_1 = require("./constants");
|
|
9
|
-
const backend_1 = require("../deploy/functions/backend");
|
|
10
10
|
const proto_1 = require("../gcp/proto");
|
|
11
|
+
const logger_1 = require("../logger");
|
|
12
|
+
const manifest_1 = require("../extensions/manifest");
|
|
11
13
|
const memoryLookup = {
|
|
12
14
|
"128MB": 128,
|
|
13
15
|
"256MB": 256,
|
|
@@ -60,30 +62,39 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
60
62
|
id: `${endpoint.region}-${endpoint.id}`,
|
|
61
63
|
};
|
|
62
64
|
(0, proto_1.copyIfPresent)(def, endpoint, "timeout", "availableMemoryMb", "labels", "platform", "secretEnvironmentVariables");
|
|
63
|
-
if (
|
|
65
|
+
if (backend.isHttpsTriggered(endpoint)) {
|
|
64
66
|
def.httpsTrigger = endpoint.httpsTrigger;
|
|
65
67
|
}
|
|
66
|
-
else if (
|
|
68
|
+
else if (backend.isCallableTriggered(endpoint)) {
|
|
69
|
+
def.httpsTrigger = {};
|
|
70
|
+
def.labels = Object.assign(Object.assign({}, def.labels), { "deployment-callable": "true" });
|
|
71
|
+
}
|
|
72
|
+
else if (backend.isEventTriggered(endpoint)) {
|
|
67
73
|
const eventTrigger = endpoint.eventTrigger;
|
|
68
74
|
if (endpoint.platform === "gcfv1") {
|
|
75
|
+
const resourceFilter = backend.findEventFilter(endpoint, "resource");
|
|
76
|
+
if (!resourceFilter) {
|
|
77
|
+
logger_1.logger.debug(`Invalid event trigger ${JSON.stringify(endpoint)}, expected event filter with resource attribute. Skipping.`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
69
80
|
def.eventTrigger = {
|
|
70
81
|
eventType: eventTrigger.eventType,
|
|
71
|
-
resource:
|
|
82
|
+
resource: resourceFilter.value,
|
|
72
83
|
};
|
|
73
84
|
}
|
|
74
85
|
else {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
const [eventFilter] = endpoint.eventTrigger.eventFilters;
|
|
87
|
+
if (!eventFilter) {
|
|
88
|
+
logger_1.logger.debug(`Invalid event trigger ${JSON.stringify(endpoint)}, expected at least one event filter. Skipping.`);
|
|
78
89
|
continue;
|
|
79
90
|
}
|
|
80
91
|
def.eventTrigger = {
|
|
81
92
|
eventType: eventTrigger.eventType,
|
|
82
|
-
resource:
|
|
93
|
+
resource: eventFilter.value,
|
|
83
94
|
};
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
|
-
else if (
|
|
97
|
+
else if (backend.isScheduleTriggered(endpoint)) {
|
|
87
98
|
def.eventTrigger = { eventType: "pubsub", resource: "" };
|
|
88
99
|
def.schedule = endpoint.scheduleTrigger;
|
|
89
100
|
}
|
|
@@ -94,7 +105,7 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
94
105
|
return regionDefinitions;
|
|
95
106
|
}
|
|
96
107
|
exports.emulatedFunctionsFromEndpoints = emulatedFunctionsFromEndpoints;
|
|
97
|
-
function emulatedFunctionsByRegion(definitions) {
|
|
108
|
+
function emulatedFunctionsByRegion(definitions, secretEnvVariables = []) {
|
|
98
109
|
const regionDefinitions = [];
|
|
99
110
|
for (const def of definitions) {
|
|
100
111
|
if (!def.regions) {
|
|
@@ -106,6 +117,7 @@ function emulatedFunctionsByRegion(definitions) {
|
|
|
106
117
|
defDeepCopy.region = region;
|
|
107
118
|
defDeepCopy.id = `${region}-${defDeepCopy.name}`;
|
|
108
119
|
defDeepCopy.platform = defDeepCopy.platform || "gcfv1";
|
|
120
|
+
defDeepCopy.secretEnvironmentVariables = secretEnvVariables;
|
|
109
121
|
regionDefinitions.push(defDeepCopy);
|
|
110
122
|
}
|
|
111
123
|
}
|
|
@@ -219,3 +231,14 @@ function getSignatureType(def) {
|
|
|
219
231
|
return def.platform === "gcfv2" ? "cloudevent" : "event";
|
|
220
232
|
}
|
|
221
233
|
exports.getSignatureType = getSignatureType;
|
|
234
|
+
const LOCAL_SECRETS_FILE = ".secret.local";
|
|
235
|
+
function getSecretLocalPath(backend, projectDir) {
|
|
236
|
+
const secretsFile = backend.extensionInstanceId
|
|
237
|
+
? `${backend.extensionInstanceId}${LOCAL_SECRETS_FILE}`
|
|
238
|
+
: LOCAL_SECRETS_FILE;
|
|
239
|
+
const secretDirectory = backend.extensionInstanceId
|
|
240
|
+
? path.join(projectDir, manifest_1.ENV_DIRECTORY)
|
|
241
|
+
: backend.functionsDir;
|
|
242
|
+
return path.join(secretDirectory, secretsFile);
|
|
243
|
+
}
|
|
244
|
+
exports.getSecretLocalPath = getSecretLocalPath;
|