firebase-tools 10.2.1 → 10.3.1
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/appdistribution/options-parser-util.js +1 -1
- package/lib/auth.js +3 -3
- 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/database-rules-list.js +2 -2
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/ext-configure.js +67 -7
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +7 -2
- package/lib/commands/ext-install.js +173 -109
- package/lib/commands/ext-uninstall.js +17 -8
- package/lib/commands/ext-update.js +67 -12
- package/lib/commands/functions-config-export.js +1 -1
- package/lib/commands/hosting-clone.js +3 -3
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/config.js +6 -3
- package/lib/deploy/extensions/deploymentSummary.js +3 -3
- package/lib/deploy/extensions/planner.js +7 -6
- package/lib/deploy/extensions/tasks.js +1 -1
- package/lib/deploy/functions/backend.js +21 -5
- package/lib/deploy/functions/checkIam.js +5 -5
- package/lib/deploy/functions/containerCleaner.js +3 -3
- package/lib/deploy/functions/ensure.js +3 -3
- package/lib/deploy/functions/functionsDeployHelper.js +2 -2
- package/lib/deploy/functions/prepare.js +5 -3
- package/lib/deploy/functions/pricing.js +1 -1
- package/lib/deploy/functions/prompts.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +7 -7
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/planner.js +43 -26
- 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 +22 -12
- package/lib/deploy/functions/runtimes/golang/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/index.js +53 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +64 -20
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- 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/deploy/functions/validate.js +3 -3
- package/lib/deploy/hosting/deploy.js +2 -2
- package/lib/deploy/hosting/hashcache.js +21 -19
- 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/constants.js +4 -0
- package/lib/emulator/controller.js +54 -24
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +1 -1
- package/lib/emulator/emulatorLogger.js +12 -1
- package/lib/emulator/extensions/validation.js +70 -0
- package/lib/emulator/extensionsEmulator.js +175 -0
- package/lib/emulator/functionsEmulator.js +95 -43
- package/lib/emulator/functionsEmulatorRuntime.js +44 -36
- package/lib/emulator/functionsEmulatorShared.js +30 -11
- package/lib/emulator/functionsEmulatorShell.js +1 -1
- package/lib/emulator/functionsEmulatorUtils.js +4 -4
- package/lib/emulator/functionsRuntimeWorker.js +2 -2
- 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 +314 -332
- package/lib/emulator/storage/apis/gcloud.js +241 -121
- package/lib/emulator/storage/crc.js +5 -1
- package/lib/emulator/storage/errors.js +9 -0
- package/lib/emulator/storage/files.js +159 -300
- package/lib/emulator/storage/index.js +27 -73
- package/lib/emulator/storage/metadata.js +65 -51
- 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/runtime.js +8 -7
- 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/emulator/types.js +3 -0
- package/lib/ensureApiEnabled.js +5 -1
- package/lib/error.js +1 -1
- package/lib/extensions/askUserForParam.js +31 -25
- package/lib/extensions/changelog.js +3 -1
- package/lib/extensions/checkProjectBilling.js +1 -1
- package/lib/extensions/displayExtensionInfo.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +56 -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 +32 -19
- package/lib/extensions/manifest.js +144 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +34 -12
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/secretsUtils.js +3 -3
- package/lib/functional.js +1 -1
- package/lib/functions/env.js +6 -7
- package/lib/functions/events/v2.js +11 -0
- package/lib/gcp/cloudfunctions.js +43 -11
- package/lib/gcp/cloudfunctionsv2.js +48 -17
- package/lib/gcp/cloudtasks.js +1 -1
- package/lib/gcp/docker.js +2 -2
- package/lib/gcp/resourceManager.js +4 -4
- package/lib/gcp/run.js +2 -2
- package/lib/hosting/api.js +1 -1
- 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/serve/functions.js +2 -1
- package/lib/utils.js +15 -2
- package/npm-shrinkwrap.json +786 -393
- package/package.json +1 -1
- package/schema/firebase-config.json +5 -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
- package/lib/deploy/extensions/params.js +0 -39
- package/lib/deploy/functions/eventTypes.js +0 -10
|
@@ -33,10 +33,17 @@ 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");
|
|
39
|
+
const extensionsEmulator_1 = require("./extensionsEmulator");
|
|
40
|
+
const previews_1 = require("../previews");
|
|
41
|
+
const START_LOGGING_EMULATOR = utils.envOverride("START_LOGGING_EMULATOR", "false", (val) => val === "true");
|
|
38
42
|
async function getAndCheckAddress(emulator, options) {
|
|
39
43
|
var _a, _b, _c, _d;
|
|
44
|
+
if (emulator === types_1.Emulators.EXTENSIONS) {
|
|
45
|
+
emulator = types_1.Emulators.FUNCTIONS;
|
|
46
|
+
}
|
|
40
47
|
let host = ((_b = (_a = options.config.src.emulators) === null || _a === void 0 ? void 0 : _a[emulator]) === null || _b === void 0 ? void 0 : _b.host) || constants_1.Constants.getDefaultHost(emulator);
|
|
41
48
|
if (host === "localhost" && utils.isRunningInWSL()) {
|
|
42
49
|
host = "127.0.0.1";
|
|
@@ -56,7 +63,7 @@ async function getAndCheckAddress(emulator, options) {
|
|
|
56
63
|
if (!portOpen) {
|
|
57
64
|
if (findAvailablePort) {
|
|
58
65
|
const newPort = await portUtils.findAvailablePort(host, port);
|
|
59
|
-
if (newPort
|
|
66
|
+
if (newPort !== port) {
|
|
60
67
|
loggerForEmulator.logLabeled("WARN", emulator, `${constants_1.Constants.description(emulator)} unable to start on port ${port}, starting on ${newPort} instead.`);
|
|
61
68
|
port = newPort;
|
|
62
69
|
}
|
|
@@ -86,7 +93,7 @@ async function getAndCheckAddress(emulator, options) {
|
|
|
86
93
|
}
|
|
87
94
|
async function startEmulator(instance) {
|
|
88
95
|
const name = instance.getName();
|
|
89
|
-
track("Emulator Run", name);
|
|
96
|
+
void track("Emulator Run", name);
|
|
90
97
|
await registry_1.EmulatorRegistry.start(instance);
|
|
91
98
|
}
|
|
92
99
|
exports.startEmulator = startEmulator;
|
|
@@ -115,7 +122,11 @@ async function cleanShutdown() {
|
|
|
115
122
|
}
|
|
116
123
|
exports.cleanShutdown = cleanShutdown;
|
|
117
124
|
function filterEmulatorTargets(options) {
|
|
118
|
-
let targets = types_1.ALL_SERVICE_EMULATORS
|
|
125
|
+
let targets = [...types_1.ALL_SERVICE_EMULATORS];
|
|
126
|
+
if (previews_1.previews.extensionsemulator) {
|
|
127
|
+
targets.push(types_1.Emulators.EXTENSIONS);
|
|
128
|
+
}
|
|
129
|
+
targets = targets.filter((e) => {
|
|
119
130
|
return options.config.has(e) || options.config.has(`emulators.${e}`);
|
|
120
131
|
});
|
|
121
132
|
const onlyOptions = options.only;
|
|
@@ -222,7 +233,7 @@ async function startAll(options, showUI = true) {
|
|
|
222
233
|
if (shouldStart(options, types_1.Emulators.HUB)) {
|
|
223
234
|
const hubAddr = await getAndCheckAddress(types_1.Emulators.HUB, options);
|
|
224
235
|
const hub = new hub_1.EmulatorHub(Object.assign({ projectId }, hubAddr));
|
|
225
|
-
track("emulators:start", "hub");
|
|
236
|
+
void track("emulators:start", "hub");
|
|
226
237
|
await startEmulator(hub);
|
|
227
238
|
}
|
|
228
239
|
let exportMetadata = {
|
|
@@ -239,19 +250,47 @@ async function startAll(options, showUI = true) {
|
|
|
239
250
|
hubLogger.logLabeled("WARN", "emulators", `Could not find import/export metadata file, ${clc.bold("skipping data import!")}`);
|
|
240
251
|
}
|
|
241
252
|
}
|
|
253
|
+
const emulatableBackends = [];
|
|
254
|
+
const projectDir = (options.extDevDir || options.config.projectDir);
|
|
242
255
|
if (shouldStart(options, types_1.Emulators.FUNCTIONS)) {
|
|
243
|
-
const functionsLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
|
|
244
|
-
const functionsAddr = await getAndCheckAddress(types_1.Emulators.FUNCTIONS, options);
|
|
245
|
-
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
246
256
|
utils.assertDefined(options.config.src.functions);
|
|
247
257
|
utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
|
|
248
|
-
utils.assertIsStringOrUndefined(options.
|
|
249
|
-
const projectDir = options.extensionDir || options.config.projectDir;
|
|
258
|
+
utils.assertIsStringOrUndefined(options.extDevDir);
|
|
250
259
|
const functionsDir = path.join(projectDir, options.config.src.functions.source);
|
|
260
|
+
emulatableBackends.push({
|
|
261
|
+
functionsDir,
|
|
262
|
+
env: Object.assign({}, options.extDevEnv),
|
|
263
|
+
secretEnv: [],
|
|
264
|
+
predefinedTriggers: options.extDevTriggers,
|
|
265
|
+
nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion || options.config.get("functions.runtime")),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (shouldStart(options, types_1.Emulators.EXTENSIONS) && previews_1.previews.extensionsemulator) {
|
|
269
|
+
const projectNumber = constants_1.Constants.isDemoProject(projectId)
|
|
270
|
+
? constants_1.Constants.FAKE_PROJECT_NUMBER
|
|
271
|
+
: await (0, projectUtils_1.needProjectNumber)(options);
|
|
272
|
+
const aliases = (0, projectUtils_1.getAliases)(options, projectId);
|
|
273
|
+
const extensionEmulator = new extensionsEmulator_1.ExtensionsEmulator({
|
|
274
|
+
projectId,
|
|
275
|
+
projectDir: options.config.projectDir,
|
|
276
|
+
projectNumber,
|
|
277
|
+
aliases,
|
|
278
|
+
extensions: options.config.get("extensions"),
|
|
279
|
+
});
|
|
280
|
+
const extensionsBackends = await extensionEmulator.getExtensionBackends();
|
|
281
|
+
const filteredExtensionsBackends = extensionEmulator.filterUnemulatedTriggers(options, extensionsBackends);
|
|
282
|
+
emulatableBackends.push(...filteredExtensionsBackends);
|
|
283
|
+
void track("Emulator Run", types_1.Emulators.EXTENSIONS);
|
|
284
|
+
registry_1.EmulatorRegistry.registerExtensionsEmulator();
|
|
285
|
+
}
|
|
286
|
+
if (emulatableBackends.length) {
|
|
287
|
+
const functionsLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
|
|
288
|
+
const functionsAddr = await getAndCheckAddress(types_1.Emulators.FUNCTIONS, options);
|
|
289
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
251
290
|
let inspectFunctions;
|
|
252
291
|
if (options.inspectFunctions) {
|
|
253
292
|
inspectFunctions = commandUtils.parseInspectionPort(options);
|
|
254
|
-
functionsLogger.logLabeled("WARN", "functions", `You are running the
|
|
293
|
+
functionsLogger.logLabeled("WARN", "functions", `You are running the Functions emulator in debug mode (port=${inspectFunctions}). This means that functions will execute in sequence rather than in parallel.`);
|
|
255
294
|
}
|
|
256
295
|
const emulatorsNotRunning = types_1.ALL_SERVICE_EMULATORS.filter((e) => {
|
|
257
296
|
return e !== types_1.Emulators.FUNCTIONS && !shouldStart(options, e);
|
|
@@ -260,14 +299,6 @@ async function startAll(options, showUI = true) {
|
|
|
260
299
|
functionsLogger.logLabeled("WARN", "functions", `The following emulators are not running, calls to these services from the Functions emulator will affect production: ${clc.bold(emulatorsNotRunning.join(", "))}`);
|
|
261
300
|
}
|
|
262
301
|
const account = (0, auth_2.getProjectDefaultAccount)(options.projectRoot);
|
|
263
|
-
const emulatableBackends = [
|
|
264
|
-
{
|
|
265
|
-
functionsDir,
|
|
266
|
-
env: Object.assign({}, options.extensionEnv),
|
|
267
|
-
predefinedTriggers: options.extensionTriggers,
|
|
268
|
-
nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extensionNodeVersion || options.config.get("functions.runtime")),
|
|
269
|
-
},
|
|
270
|
-
];
|
|
271
302
|
const functionsEmulator = new functionsEmulator_1.FunctionsEmulator({
|
|
272
303
|
projectId,
|
|
273
304
|
projectDir,
|
|
@@ -276,6 +307,7 @@ async function startAll(options, showUI = true) {
|
|
|
276
307
|
host: functionsAddr.host,
|
|
277
308
|
port: functionsAddr.port,
|
|
278
309
|
debugPort: inspectFunctions,
|
|
310
|
+
projectAlias: options.projectAlias,
|
|
279
311
|
});
|
|
280
312
|
await startEmulator(functionsEmulator);
|
|
281
313
|
}
|
|
@@ -395,15 +427,11 @@ async function startAll(options, showUI = true) {
|
|
|
395
427
|
}
|
|
396
428
|
if (shouldStart(options, types_1.Emulators.STORAGE)) {
|
|
397
429
|
const storageAddr = await getAndCheckAddress(types_1.Emulators.STORAGE, options);
|
|
398
|
-
const storageConfig = options.config.data.storage;
|
|
399
|
-
if (!(storageConfig === null || storageConfig === void 0 ? void 0 : storageConfig.rules)) {
|
|
400
|
-
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");
|
|
401
|
-
}
|
|
402
430
|
const storageEmulator = new storage_1.StorageEmulator({
|
|
403
431
|
host: storageAddr.host,
|
|
404
432
|
port: storageAddr.port,
|
|
405
433
|
projectId: projectId,
|
|
406
|
-
rules:
|
|
434
|
+
rules: (0, config_1.getStorageRulesConfig)(projectId, options),
|
|
407
435
|
});
|
|
408
436
|
await startEmulator(storageEmulator);
|
|
409
437
|
if (exportMetadata.storage) {
|
|
@@ -425,13 +453,15 @@ async function startAll(options, showUI = true) {
|
|
|
425
453
|
if (showUI && !shouldStart(options, types_1.Emulators.UI)) {
|
|
426
454
|
hubLogger.logLabeled("WARN", "emulators", "The Emulator UI requires a project ID to start. Configure your default project with 'firebase use' or pass the --project flag.");
|
|
427
455
|
}
|
|
428
|
-
if (showUI && shouldStart(options, types_1.Emulators.UI)) {
|
|
456
|
+
if (showUI && (shouldStart(options, types_1.Emulators.UI) || START_LOGGING_EMULATOR)) {
|
|
429
457
|
const loggingAddr = await getAndCheckAddress(types_1.Emulators.LOGGING, options);
|
|
430
458
|
const loggingEmulator = new loggingEmulator_1.LoggingEmulator({
|
|
431
459
|
host: loggingAddr.host,
|
|
432
460
|
port: loggingAddr.port,
|
|
433
461
|
});
|
|
434
462
|
await startEmulator(loggingEmulator);
|
|
463
|
+
}
|
|
464
|
+
if (showUI && shouldStart(options, types_1.Emulators.UI)) {
|
|
435
465
|
const uiAddr = await getAndCheckAddress(types_1.Emulators.UI, options);
|
|
436
466
|
const ui = new ui_1.EmulatorUI(Object.assign({ projectId: projectId, auto_download: true }, uiAddr));
|
|
437
467
|
await startEmulator(ui);
|
package/lib/emulator/download.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.downloadEmulator = void 0;
|
|
3
|
+
exports.downloadExtensionVersion = exports.downloadEmulator = void 0;
|
|
4
4
|
const crypto = require("crypto");
|
|
5
5
|
const fs = require("fs-extra");
|
|
6
6
|
const path = require("path");
|
|
@@ -32,6 +32,23 @@ async function downloadEmulator(name) {
|
|
|
32
32
|
removeOldFiles(name, emulator);
|
|
33
33
|
}
|
|
34
34
|
exports.downloadEmulator = downloadEmulator;
|
|
35
|
+
async function downloadExtensionVersion(extensionVersionRef, sourceDownloadUri, targetDir) {
|
|
36
|
+
const emulatorLogger = emulatorLogger_1.EmulatorLogger.forExtension({ ref: extensionVersionRef });
|
|
37
|
+
emulatorLogger.logLabeled("BULLET", "extensions", `Starting download for ${extensionVersionRef} source code...`);
|
|
38
|
+
try {
|
|
39
|
+
fs.mkdirSync(targetDir);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
emulatorLogger.logLabeled("BULLET", "extensions", `cache directory for ${extensionVersionRef} already exists...`);
|
|
43
|
+
}
|
|
44
|
+
emulatorLogger.logLabeled("BULLET", "extensions", `downloading ${sourceDownloadUri}...`);
|
|
45
|
+
const sourceCodeZip = await downloadUtils.downloadToTmp(sourceDownloadUri);
|
|
46
|
+
await unzip(sourceCodeZip, targetDir);
|
|
47
|
+
fs.chmodSync(targetDir, 0o755);
|
|
48
|
+
emulatorLogger.logLabeled("BULLET", "extensions", `Downloaded to ${targetDir}...`);
|
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
50
|
+
}
|
|
51
|
+
exports.downloadExtensionVersion = downloadExtensionVersion;
|
|
35
52
|
function unzip(zipPath, unzipDir) {
|
|
36
53
|
return new Promise((resolve, reject) => {
|
|
37
54
|
fs.createReadStream(zipPath)
|
|
@@ -34,7 +34,7 @@ class EmulatorLogger {
|
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
static forFunction(functionName) {
|
|
37
|
+
static forFunction(functionName, extensionLogInfo) {
|
|
38
38
|
return new EmulatorLogger({
|
|
39
39
|
metadata: {
|
|
40
40
|
emulator: {
|
|
@@ -43,6 +43,17 @@ class EmulatorLogger {
|
|
|
43
43
|
function: {
|
|
44
44
|
name: functionName,
|
|
45
45
|
},
|
|
46
|
+
extension: extensionLogInfo,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
static forExtension(extensionLogInfo) {
|
|
51
|
+
return new EmulatorLogger({
|
|
52
|
+
metadata: {
|
|
53
|
+
emulator: {
|
|
54
|
+
name: "extensions",
|
|
55
|
+
},
|
|
56
|
+
extension: extensionLogInfo,
|
|
46
57
|
},
|
|
47
58
|
});
|
|
48
59
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkForUnemulatedTriggerTypes = exports.getUnemulatedAPIs = void 0;
|
|
4
|
+
const planner = require("../../deploy/extensions/planner");
|
|
5
|
+
const controller_1 = require("../controller");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
7
|
+
const ensureApiEnabled_1 = require("../../ensureApiEnabled");
|
|
8
|
+
const functionsEmulatorShared_1 = require("../functionsEmulatorShared");
|
|
9
|
+
const types_1 = require("../types");
|
|
10
|
+
const EMULATED_APIS = [
|
|
11
|
+
"storage-component.googleapis.com",
|
|
12
|
+
"firestore.googleapis.com",
|
|
13
|
+
"pubsub.googleapis.com",
|
|
14
|
+
"identitytoolkit.googleapis.com",
|
|
15
|
+
];
|
|
16
|
+
async function getUnemulatedAPIs(projectId, instances) {
|
|
17
|
+
var _a;
|
|
18
|
+
const unemulatedAPIs = {};
|
|
19
|
+
for (const i of instances) {
|
|
20
|
+
const extensionVersion = await planner.getExtensionVersion(i);
|
|
21
|
+
for (const api of (_a = extensionVersion.spec.apis) !== null && _a !== void 0 ? _a : []) {
|
|
22
|
+
if (!EMULATED_APIS.includes(api.apiName)) {
|
|
23
|
+
if (unemulatedAPIs[api.apiName]) {
|
|
24
|
+
unemulatedAPIs[api.apiName].instanceIds.push(i.instanceId);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const enabled = !constants_1.Constants.isDemoProject(projectId) &&
|
|
28
|
+
(await (0, ensureApiEnabled_1.check)(projectId, api.apiName, "extensions", true));
|
|
29
|
+
unemulatedAPIs[api.apiName] = {
|
|
30
|
+
apiName: api.apiName,
|
|
31
|
+
instanceIds: [i.instanceId],
|
|
32
|
+
enabled,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return Object.values(unemulatedAPIs);
|
|
39
|
+
}
|
|
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;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExtensionsEmulator = void 0;
|
|
4
|
+
const fs = require("fs-extra");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const clc = require("cli-color");
|
|
8
|
+
const Table = require("cli-table");
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const planner = require("../deploy/extensions/planner");
|
|
11
|
+
const error_1 = require("../error");
|
|
12
|
+
const refs_1 = require("../extensions/refs");
|
|
13
|
+
const download_1 = require("./download");
|
|
14
|
+
const optionsHelper_1 = require("../extensions/emulator/optionsHelper");
|
|
15
|
+
const emulatorLogger_1 = require("./emulatorLogger");
|
|
16
|
+
const types_1 = require("./types");
|
|
17
|
+
const validation_1 = require("./extensions/validation");
|
|
18
|
+
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
19
|
+
const shortenUrl_1 = require("../shortenUrl");
|
|
20
|
+
const constants_1 = require("./constants");
|
|
21
|
+
class ExtensionsEmulator {
|
|
22
|
+
constructor(args) {
|
|
23
|
+
this.want = [];
|
|
24
|
+
this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.EXTENSIONS);
|
|
25
|
+
this.pendingDownloads = new Map();
|
|
26
|
+
this.args = args;
|
|
27
|
+
}
|
|
28
|
+
async readManifest() {
|
|
29
|
+
var _a;
|
|
30
|
+
this.want = await planner.want({
|
|
31
|
+
projectId: this.args.projectId,
|
|
32
|
+
projectNumber: this.args.projectNumber,
|
|
33
|
+
aliases: (_a = this.args.aliases) !== null && _a !== void 0 ? _a : [],
|
|
34
|
+
projectDir: this.args.projectDir,
|
|
35
|
+
extensions: this.args.extensions,
|
|
36
|
+
emulatorMode: true,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async ensureSourceCode(instance) {
|
|
40
|
+
if (!instance.ref) {
|
|
41
|
+
throw new error_1.FirebaseError(`No ref found for ${instance.instanceId}. Emulating local extensions is not yet supported.`);
|
|
42
|
+
}
|
|
43
|
+
const ref = (0, refs_1.toExtensionVersionRef)(instance.ref);
|
|
44
|
+
const cacheDir = process.env.FIREBASE_EXTENSIONS_CACHE_PATH ||
|
|
45
|
+
path.join(os.homedir(), ".cache", "firebase", "extensions");
|
|
46
|
+
const sourceCodePath = path.join(cacheDir, ref);
|
|
47
|
+
if (this.pendingDownloads.get(ref)) {
|
|
48
|
+
await this.pendingDownloads.get(ref);
|
|
49
|
+
}
|
|
50
|
+
if (!this.hasValidSource({ path: sourceCodePath, extRef: ref })) {
|
|
51
|
+
const promise = this.downloadSource(instance, ref, sourceCodePath);
|
|
52
|
+
this.pendingDownloads.set(ref, promise);
|
|
53
|
+
await promise;
|
|
54
|
+
}
|
|
55
|
+
return sourceCodePath;
|
|
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
|
+
}
|
|
62
|
+
hasValidSource(args) {
|
|
63
|
+
const requiredFiles = [
|
|
64
|
+
"./extension.yaml",
|
|
65
|
+
"./functions/package.json",
|
|
66
|
+
"./functions/node_modules",
|
|
67
|
+
];
|
|
68
|
+
for (const requiredFile of requiredFiles) {
|
|
69
|
+
const f = path.join(args.path, requiredFile);
|
|
70
|
+
if (!fs.existsSync(f)) {
|
|
71
|
+
emulatorLogger_1.EmulatorLogger.forExtension({ ref: args.extRef }).logLabeled("BULLET", "extensions", `Detected invalid source code for ${args.extRef}, expected to find ${f}`);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
installAndBuildSourceCode(sourceCodePath) {
|
|
78
|
+
const npmInstall = (0, child_process_1.spawnSync)("npm", ["--prefix", `/${sourceCodePath}/functions/`, "install"], {
|
|
79
|
+
encoding: "utf8",
|
|
80
|
+
});
|
|
81
|
+
if (npmInstall.error) {
|
|
82
|
+
throw npmInstall.error;
|
|
83
|
+
}
|
|
84
|
+
const npmRunGCPBuild = (0, child_process_1.spawnSync)("npm", ["--prefix", `/${sourceCodePath}/functions/`, "run", "gcp-build"], { encoding: "utf8" });
|
|
85
|
+
if (npmRunGCPBuild.error) {
|
|
86
|
+
throw npmRunGCPBuild.error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async getExtensionBackends() {
|
|
90
|
+
await this.readManifest();
|
|
91
|
+
await this.checkAndWarnAPIs(this.want);
|
|
92
|
+
return Promise.all(this.want.map((i) => {
|
|
93
|
+
return this.toEmulatableBackend(i);
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
async toEmulatableBackend(instance) {
|
|
97
|
+
const extensionDir = await this.ensureSourceCode(instance);
|
|
98
|
+
const functionsDir = path.join(extensionDir, "functions");
|
|
99
|
+
const env = Object.assign(this.autoPopulatedParams(instance), instance.params);
|
|
100
|
+
const { extensionTriggers, nodeMajorVersion, nonSecretEnv, secretEnvVariables } = await (0, optionsHelper_1.getExtensionFunctionInfo)(extensionDir, instance.instanceId, env);
|
|
101
|
+
const extension = await planner.getExtension(instance);
|
|
102
|
+
const extensionVersion = await planner.getExtensionVersion(instance);
|
|
103
|
+
return {
|
|
104
|
+
functionsDir,
|
|
105
|
+
env: nonSecretEnv,
|
|
106
|
+
secretEnv: secretEnvVariables,
|
|
107
|
+
predefinedTriggers: extensionTriggers,
|
|
108
|
+
nodeMajorVersion: nodeMajorVersion,
|
|
109
|
+
extensionInstanceId: instance.instanceId,
|
|
110
|
+
extension,
|
|
111
|
+
extensionVersion,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
autoPopulatedParams(instance) {
|
|
115
|
+
const projectId = this.args.projectId;
|
|
116
|
+
return {
|
|
117
|
+
PROJECT_ID: projectId !== null && projectId !== void 0 ? projectId : "",
|
|
118
|
+
EXT_INSTANCE_ID: instance.instanceId,
|
|
119
|
+
DATABASE_INSTANCE: projectId !== null && projectId !== void 0 ? projectId : "",
|
|
120
|
+
DATABASE_URL: `https://${projectId}.firebaseio.com`,
|
|
121
|
+
STORAGE_BUCKET: `${projectId}.appspot.com`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async checkAndWarnAPIs(instances) {
|
|
125
|
+
const apisToWarn = await (0, validation_1.getUnemulatedAPIs)(this.args.projectId, instances);
|
|
126
|
+
if (apisToWarn.length) {
|
|
127
|
+
const table = new Table({
|
|
128
|
+
head: [
|
|
129
|
+
"API Name",
|
|
130
|
+
"Instances using this API",
|
|
131
|
+
`Enabled on ${this.args.projectId}`,
|
|
132
|
+
`Enable this API`,
|
|
133
|
+
],
|
|
134
|
+
style: { head: ["yellow"] },
|
|
135
|
+
});
|
|
136
|
+
for (const apiToWarn of apisToWarn) {
|
|
137
|
+
const enablementUri = await (0, shortenUrl_1.shortenUrl)((0, ensureApiEnabled_1.enableApiURI)(this.args.projectId, apiToWarn.apiName));
|
|
138
|
+
table.push([
|
|
139
|
+
apiToWarn.apiName,
|
|
140
|
+
apiToWarn.instanceIds,
|
|
141
|
+
apiToWarn.enabled ? "Yes" : "No",
|
|
142
|
+
apiToWarn.enabled ? "" : clc.bold.underline(enablementUri),
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
if (constants_1.Constants.isDemoProject(this.args.projectId)) {
|
|
146
|
+
this.logger.logLabeled("WARN", "Extensions", "The following Extensions make calls to Google Cloud APIs that do not have Emulators. " +
|
|
147
|
+
`${clc.bold(this.args.projectId)} is a demo project, so these Extensions may not work as expected.\n` +
|
|
148
|
+
table.toString());
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.logger.logLabeled("WARN", "Extensions", "The following Extensions make calls to Google Cloud APIs that do not have Emulators. " +
|
|
152
|
+
`These calls will go to production Google Cloud APIs which may have real effects on ${clc.bold(this.args.projectId)}.\n` +
|
|
153
|
+
table.toString());
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
filterUnemulatedTriggers(options, backends) {
|
|
158
|
+
let foundUnemulatedTrigger = false;
|
|
159
|
+
const filteredBackends = backends.filter((backend) => {
|
|
160
|
+
const unemulatedServices = (0, validation_1.checkForUnemulatedTriggerTypes)(backend, options);
|
|
161
|
+
if (unemulatedServices.length) {
|
|
162
|
+
foundUnemulatedTrigger = true;
|
|
163
|
+
const msg = ` ignored becuase it includes ${unemulatedServices.join(", ")} triggered functions, and the ${unemulatedServices.join(", ")} emulator does not exist or is not running.`;
|
|
164
|
+
this.logger.logLabeled("WARN", `extensions[${backend.extensionInstanceId}]`, msg);
|
|
165
|
+
}
|
|
166
|
+
return unemulatedServices.length === 0;
|
|
167
|
+
});
|
|
168
|
+
if (foundUnemulatedTrigger) {
|
|
169
|
+
const msg = "No Cloud Functions for these instances will be emulated, because partially emulating an Extension can lead to unexpected behavior. ";
|
|
170
|
+
this.logger.log("WARN", msg);
|
|
171
|
+
}
|
|
172
|
+
return filteredBackends;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.ExtensionsEmulator = ExtensionsEmulator;
|