firebase-tools 10.7.0 → 10.8.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 +26 -15
- package/lib/commands/ext-export.js +14 -5
- package/lib/commands/ext-install.js +31 -2
- package/lib/commands/ext-update.js +17 -10
- package/lib/commands/functions-delete.js +9 -2
- package/lib/commands/functions-secrets-set.js +1 -13
- package/lib/deploy/extensions/planner.js +12 -0
- package/lib/deploy/extensions/tasks.js +13 -0
- package/lib/deploy/functions/backend.js +67 -10
- package/lib/deploy/functions/build.js +28 -9
- package/lib/deploy/functions/checkIam.js +71 -56
- package/lib/deploy/functions/containerCleaner.js +8 -7
- package/lib/deploy/functions/deploy.js +49 -27
- package/lib/deploy/functions/functionsDeployHelper.js +48 -4
- package/lib/deploy/functions/prepare.js +125 -74
- package/lib/deploy/functions/pricing.js +2 -2
- package/lib/deploy/functions/release/executor.js +1 -1
- package/lib/deploy/functions/release/fabricator.js +94 -36
- package/lib/deploy/functions/release/index.js +16 -27
- package/lib/deploy/functions/release/planner.js +12 -7
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +13 -1
- package/lib/deploy/functions/runtimes/golang/index.js +3 -0
- package/lib/deploy/functions/runtimes/node/index.js +7 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +108 -1
- package/lib/deploy/functions/services/storage.js +6 -12
- package/lib/deploy/functions/validate.js +58 -8
- package/lib/deploy/hosting/convertConfig.js +6 -4
- package/lib/emulator/auth/cloudFunctions.js +6 -2
- package/lib/emulator/auth/operations.js +0 -1
- package/lib/emulator/auth/server.js +8 -1
- package/lib/emulator/auth/state.js +27 -24
- package/lib/emulator/controller.js +12 -9
- package/lib/emulator/databaseEmulator.js +36 -3
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/extensionsEmulator.js +3 -0
- package/lib/emulator/functionsEmulator.js +11 -9
- package/lib/emulator/functionsEmulatorRuntime.js +1 -1
- package/lib/emulator/functionsEmulatorShared.js +5 -1
- package/lib/emulator/functionsEmulatorShell.js +2 -3
- package/lib/emulator/functionsEmulatorUtils.js +5 -1
- package/lib/emulator/pubsubEmulator.js +13 -9
- package/lib/emulator/storage/apis/firebase.js +26 -4
- package/lib/ensureApiEnabled.js +1 -1
- package/lib/extensions/askUserForEventsConfig.js +97 -0
- package/lib/extensions/export.js +7 -0
- package/lib/extensions/extensionsApi.js +47 -7
- package/lib/extensions/manifest.js +1 -1
- package/lib/extensions/paramHelper.js +2 -0
- package/lib/extensions/updateHelper.js +7 -1
- package/lib/extensions/warnings.js +11 -4
- package/lib/functions/projectConfig.js +13 -8
- package/lib/functionsShellCommandAction.js +1 -1
- package/lib/gcp/cloudfunctions.js +9 -2
- package/lib/gcp/cloudfunctionsv2.js +28 -10
- package/lib/gcp/serviceusage.js +24 -0
- package/lib/previews.js +1 -1
- package/lib/serve/functions.js +16 -19
- package/lib/throttler/throttler.js +2 -1
- package/npm-shrinkwrap.json +214 -527
- package/package.json +3 -3
- package/templates/extensions/typescript/package.lint.json +2 -1
- package/templates/extensions/typescript/package.nolint.json +2 -1
- package/templates/init/functions/typescript/package.lint.json +1 -0
- package/templates/init/functions/typescript/package.nolint.json +1 -0
|
@@ -177,7 +177,9 @@ class FunctionsEmulator {
|
|
|
177
177
|
});
|
|
178
178
|
return hub;
|
|
179
179
|
}
|
|
180
|
-
async invokeTrigger(
|
|
180
|
+
async invokeTrigger(trigger, proto, runtimeOpts) {
|
|
181
|
+
const record = this.getTriggerRecordByKey(this.getTriggerKey(trigger));
|
|
182
|
+
const backend = record.backend;
|
|
181
183
|
const bundleTemplate = this.getBaseBundle();
|
|
182
184
|
const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), { proto });
|
|
183
185
|
if (this.args.debugPort) {
|
|
@@ -221,7 +223,6 @@ class FunctionsEmulator {
|
|
|
221
223
|
return Promise.resolve();
|
|
222
224
|
}
|
|
223
225
|
async connect() {
|
|
224
|
-
const loadTriggerPromises = [];
|
|
225
226
|
for (const backend of this.args.emulatableBackends) {
|
|
226
227
|
this.logger.logLabeled("BULLET", "functions", `Watching "${backend.functionsDir}" for Cloud Functions...`);
|
|
227
228
|
const watcher = chokidar.watch(backend.functionsDir, {
|
|
@@ -237,9 +238,8 @@ class FunctionsEmulator {
|
|
|
237
238
|
this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
|
|
238
239
|
return debouncedLoadTriggers();
|
|
239
240
|
});
|
|
240
|
-
|
|
241
|
+
await this.loadTriggers(backend, true);
|
|
241
242
|
}
|
|
242
|
-
await Promise.all(loadTriggerPromises);
|
|
243
243
|
await this.performPostLoadOperations();
|
|
244
244
|
return;
|
|
245
245
|
}
|
|
@@ -284,6 +284,9 @@ class FunctionsEmulator {
|
|
|
284
284
|
const discoveredBackend = await runtimeDelegate.discoverSpec(runtimeConfig, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
|
|
285
285
|
const endpoints = backend.allEndpoints(discoveredBackend);
|
|
286
286
|
(0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
|
|
287
|
+
for (const e of endpoints) {
|
|
288
|
+
e.codebase = emulatableBackend.codebase;
|
|
289
|
+
}
|
|
287
290
|
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
|
|
288
291
|
}
|
|
289
292
|
const toSetup = triggerDefinitions.filter((definition) => {
|
|
@@ -579,6 +582,7 @@ class FunctionsEmulator {
|
|
|
579
582
|
};
|
|
580
583
|
}
|
|
581
584
|
setTriggersForTesting(triggers, backend) {
|
|
585
|
+
this.triggers = {};
|
|
582
586
|
triggers.forEach((def) => this.addTriggerRecord(def, { backend, ignored: false }));
|
|
583
587
|
}
|
|
584
588
|
getBaseBundle() {
|
|
@@ -847,11 +851,9 @@ class FunctionsEmulator {
|
|
|
847
851
|
}
|
|
848
852
|
async reloadTriggers() {
|
|
849
853
|
this.triggerGeneration++;
|
|
850
|
-
const loadTriggerPromises = [];
|
|
851
854
|
for (const backend of this.args.emulatableBackends) {
|
|
852
|
-
|
|
855
|
+
await this.loadTriggers(backend);
|
|
853
856
|
}
|
|
854
|
-
await Promise.all(loadTriggerPromises);
|
|
855
857
|
await this.performPostLoadOperations();
|
|
856
858
|
return;
|
|
857
859
|
}
|
|
@@ -862,7 +864,7 @@ class FunctionsEmulator {
|
|
|
862
864
|
}
|
|
863
865
|
const trigger = record.def;
|
|
864
866
|
const service = (0, functionsEmulatorShared_1.getFunctionService)(trigger);
|
|
865
|
-
const worker = await this.invokeTrigger(
|
|
867
|
+
const worker = await this.invokeTrigger(trigger, proto);
|
|
866
868
|
return new Promise((resolve, reject) => {
|
|
867
869
|
if (projectId !== this.args.projectId) {
|
|
868
870
|
if (service !== constants_1.Constants.SERVICE_REALTIME_DATABASE) {
|
|
@@ -949,7 +951,7 @@ class FunctionsEmulator {
|
|
|
949
951
|
req.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER] = encodeURIComponent(JSON.stringify(contextAuth));
|
|
950
952
|
}
|
|
951
953
|
}
|
|
952
|
-
const worker = await this.invokeTrigger(
|
|
954
|
+
const worker = await this.invokeTrigger(trigger);
|
|
953
955
|
worker.onLogs((el) => {
|
|
954
956
|
if (el.level === "FATAL") {
|
|
955
957
|
res.status(500).send(el.text);
|
|
@@ -213,7 +213,7 @@ function initializeNetworkFiltering(frb) {
|
|
|
213
213
|
})
|
|
214
214
|
.filter((v) => v);
|
|
215
215
|
const href = (hrefs.length && hrefs[0]) || "";
|
|
216
|
-
if (href && !history[href] && !
|
|
216
|
+
if (href && !history[href] && !(0, functionsEmulatorUtils_1.isLocalHost)(href)) {
|
|
217
217
|
history[href] = true;
|
|
218
218
|
if (href.indexOf("googleapis.com") !== -1) {
|
|
219
219
|
new types_1.EmulatorLog("SYSTEM", "googleapis-network-access", "", {
|
|
@@ -66,6 +66,7 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
66
66
|
region: endpoint.region,
|
|
67
67
|
name: endpoint.id,
|
|
68
68
|
id: `${endpoint.region}-${endpoint.id}`,
|
|
69
|
+
codebase: endpoint.codebase,
|
|
69
70
|
};
|
|
70
71
|
(0, proto_1.copyIfPresent)(def, endpoint, "availableMemoryMb", "labels", "timeoutSeconds", "platform", "secretEnvironmentVariables");
|
|
71
72
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
@@ -105,6 +106,9 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
105
106
|
options: endpoint.blockingTrigger.options || {},
|
|
106
107
|
};
|
|
107
108
|
}
|
|
109
|
+
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
110
|
+
def.httpsTrigger = {};
|
|
111
|
+
}
|
|
108
112
|
else {
|
|
109
113
|
}
|
|
110
114
|
regionDefinitions.push(def);
|
|
@@ -279,7 +283,7 @@ function toBackendInfo(e, cf3Triggers) {
|
|
|
279
283
|
extension: e.extension,
|
|
280
284
|
extensionVersion: extensionVersion,
|
|
281
285
|
extensionSpec: extensionSpec,
|
|
282
|
-
functionTriggers: (_b = e.predefinedTriggers) !== null && _b !== void 0 ? _b : cf3Triggers,
|
|
286
|
+
functionTriggers: (_b = e.predefinedTriggers) !== null && _b !== void 0 ? _b : cf3Triggers.filter((t) => t.codebase === e.codebase),
|
|
283
287
|
}));
|
|
284
288
|
}
|
|
285
289
|
exports.toBackendInfo = toBackendInfo;
|
|
@@ -7,9 +7,8 @@ const utils = require("../utils");
|
|
|
7
7
|
const logger_1 = require("../logger");
|
|
8
8
|
const error_1 = require("../error");
|
|
9
9
|
class FunctionsEmulatorShell {
|
|
10
|
-
constructor(emu
|
|
10
|
+
constructor(emu) {
|
|
11
11
|
this.emu = emu;
|
|
12
|
-
this.backend = backend;
|
|
13
12
|
this.urls = {};
|
|
14
13
|
this.triggers = emu.getTriggerDefinitions();
|
|
15
14
|
this.emulatedFunctions = this.triggers.map((t) => t.id);
|
|
@@ -42,7 +41,7 @@ class FunctionsEmulatorShell {
|
|
|
42
41
|
auth: opts.auth,
|
|
43
42
|
data,
|
|
44
43
|
};
|
|
45
|
-
this.emu.invokeTrigger(
|
|
44
|
+
this.emu.invokeTrigger(trigger, proto);
|
|
46
45
|
}
|
|
47
46
|
getTrigger(name) {
|
|
48
47
|
const result = this.triggers.find((trigger) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compareVersionStrings = exports.parseVersionString = exports.parseRuntimeVersion = exports.removePathSegments = exports.trimSlashes = exports.isValidWildcardMatch = exports.extractParamsFromPath = void 0;
|
|
3
|
+
exports.isLocalHost = exports.compareVersionStrings = exports.parseVersionString = exports.parseRuntimeVersion = exports.removePathSegments = exports.trimSlashes = exports.isValidWildcardMatch = exports.extractParamsFromPath = void 0;
|
|
4
4
|
const wildcardRegex = new RegExp("{[^/{}]*}");
|
|
5
5
|
const wildcardKeyRegex = new RegExp("^{(.+)}$");
|
|
6
6
|
function extractParamsFromPath(wildcardPath, snapshotPath) {
|
|
@@ -85,3 +85,7 @@ function compareVersionStrings(a, b) {
|
|
|
85
85
|
return 0;
|
|
86
86
|
}
|
|
87
87
|
exports.compareVersionStrings = compareVersionStrings;
|
|
88
|
+
function isLocalHost(href) {
|
|
89
|
+
return !!href.match(/^(http(s)?:\/\/)?(localhost|127.0.0.1|\[::1])/);
|
|
90
|
+
}
|
|
91
|
+
exports.isLocalHost = isLocalHost;
|
|
@@ -44,14 +44,7 @@ class PubsubEmulator {
|
|
|
44
44
|
getName() {
|
|
45
45
|
return types_1.Emulators.PUBSUB;
|
|
46
46
|
}
|
|
47
|
-
async
|
|
48
|
-
this.logger.logLabeled("DEBUG", "pubsub", `addTrigger(${topicName}, ${triggerKey}, ${signatureType})`);
|
|
49
|
-
const triggers = this.triggersForTopic.get(topicName) || [];
|
|
50
|
-
if (triggers.some((t) => t.triggerKey === triggerKey) &&
|
|
51
|
-
this.subscriptionForTopic.has(topicName)) {
|
|
52
|
-
this.logger.logLabeled("DEBUG", "pubsub", "Trigger already exists");
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
47
|
+
async maybeCreateTopicAndSub(topicName) {
|
|
55
48
|
const topic = this.pubsub.topic(topicName);
|
|
56
49
|
try {
|
|
57
50
|
this.logger.logLabeled("DEBUG", "pubsub", `Creating topic: ${topicName}`);
|
|
@@ -74,7 +67,7 @@ class PubsubEmulator {
|
|
|
74
67
|
catch (e) {
|
|
75
68
|
if (e && e.code === 6) {
|
|
76
69
|
this.logger.logLabeled("DEBUG", "pubsub", `Sub for ${topicName} exists`);
|
|
77
|
-
sub = topic.subscription(
|
|
70
|
+
sub = topic.subscription(subName);
|
|
78
71
|
}
|
|
79
72
|
else {
|
|
80
73
|
throw new error_1.FirebaseError(`Could not create sub ${subName}`, { original: e });
|
|
@@ -83,6 +76,17 @@ class PubsubEmulator {
|
|
|
83
76
|
sub.on("message", (message) => {
|
|
84
77
|
this.onMessage(topicName, message);
|
|
85
78
|
});
|
|
79
|
+
return sub;
|
|
80
|
+
}
|
|
81
|
+
async addTrigger(topicName, triggerKey, signatureType) {
|
|
82
|
+
this.logger.logLabeled("DEBUG", "pubsub", `addTrigger(${topicName}, ${triggerKey}, ${signatureType})`);
|
|
83
|
+
const sub = await this.maybeCreateTopicAndSub(topicName);
|
|
84
|
+
const triggers = this.triggersForTopic.get(topicName) || [];
|
|
85
|
+
if (triggers.some((t) => t.triggerKey === triggerKey) &&
|
|
86
|
+
this.subscriptionForTopic.has(topicName)) {
|
|
87
|
+
this.logger.logLabeled("DEBUG", "pubsub", "Trigger already exists");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
86
90
|
triggers.push({ triggerKey, signatureType });
|
|
87
91
|
this.triggersForTopic.set(topicName, triggers);
|
|
88
92
|
this.subscriptionForTopic.set(topicName, sub);
|
|
@@ -118,7 +118,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
118
118
|
return res.json(new metadata_1.OutgoingFirebaseMetadata(metadata));
|
|
119
119
|
});
|
|
120
120
|
firebaseStorageAPI.get("/b/:bucketId/o", async (req, res) => {
|
|
121
|
-
var _a, _b, _c, _d
|
|
121
|
+
var _a, _b, _c, _d;
|
|
122
122
|
const maxResults = (_a = req.query.maxResults) === null || _a === void 0 ? void 0 : _a.toString();
|
|
123
123
|
let listResponse;
|
|
124
124
|
try {
|
|
@@ -144,10 +144,12 @@ function createFirebaseEndpoints(emulator) {
|
|
|
144
144
|
}
|
|
145
145
|
return res.status(200).json({
|
|
146
146
|
nextPageToken: listResponse.nextPageToken,
|
|
147
|
-
prefixes: (_c = listResponse.prefixes) !== null && _c !== void 0 ? _c : [],
|
|
148
|
-
items: (
|
|
147
|
+
prefixes: ((_c = listResponse.prefixes) !== null && _c !== void 0 ? _c : []).filter(isValidPrefix),
|
|
148
|
+
items: ((_d = listResponse.items) !== null && _d !== void 0 ? _d : [])
|
|
149
|
+
.filter((item) => isValidNonEncodedPathString(item.name))
|
|
150
|
+
.map((item) => {
|
|
149
151
|
return { name: item.name, bucket: item.bucket };
|
|
150
|
-
})
|
|
152
|
+
}),
|
|
151
153
|
});
|
|
152
154
|
});
|
|
153
155
|
const handleUpload = async (req, res) => {
|
|
@@ -467,3 +469,23 @@ function setObjectHeaders(res, metadata, headerOverride = { "Content-Encoding":
|
|
|
467
469
|
res.setHeader("Content-Language", metadata.contentLanguage);
|
|
468
470
|
}
|
|
469
471
|
}
|
|
472
|
+
function isValidPrefix(prefix) {
|
|
473
|
+
return isValidNonEncodedPathString(removeAtMostOneTrailingSlash(prefix));
|
|
474
|
+
}
|
|
475
|
+
function isValidNonEncodedPathString(path) {
|
|
476
|
+
if (path.startsWith("/")) {
|
|
477
|
+
path = path.substring(1);
|
|
478
|
+
}
|
|
479
|
+
if (!path) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
for (const pathSegment of path.split("/")) {
|
|
483
|
+
if (!pathSegment) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
function removeAtMostOneTrailingSlash(path) {
|
|
490
|
+
return path.replace(/\/$/, "");
|
|
491
|
+
}
|
package/lib/ensureApiEnabled.js
CHANGED
|
@@ -28,7 +28,7 @@ async function check(projectId, apiName, prefix, silent = false) {
|
|
|
28
28
|
exports.check = check;
|
|
29
29
|
async function enable(projectId, apiName) {
|
|
30
30
|
try {
|
|
31
|
-
await apiClient.post(`/projects/${projectId}/services/${apiName}:enable`, {
|
|
31
|
+
await apiClient.post(`/projects/${projectId}/services/${apiName}:enable`, undefined, {
|
|
32
32
|
skipLog: { resBody: true },
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.askForEventArcLocation = exports.askShouldCollectEventsConfig = exports.askForAllowedEventTypes = exports.askForEventsConfig = exports.checkAllowedEventTypesResponse = void 0;
|
|
4
|
+
const prompt_1 = require("../prompt");
|
|
5
|
+
const extensionsApi = require("../extensions/extensionsApi");
|
|
6
|
+
const utils = require("../utils");
|
|
7
|
+
const clc = require("cli-color");
|
|
8
|
+
const logger_1 = require("../logger");
|
|
9
|
+
const { marked } = require("marked");
|
|
10
|
+
function checkAllowedEventTypesResponse(response, validEvents) {
|
|
11
|
+
const validEventTypes = validEvents.map((e) => e.type);
|
|
12
|
+
if (response.length === 0) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
for (const e of response) {
|
|
16
|
+
if (!validEventTypes.includes(e)) {
|
|
17
|
+
utils.logWarning(`Unexpected event type '${e}' was configured to be emitted. This event type is not part of the extension spec.`);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
exports.checkAllowedEventTypesResponse = checkAllowedEventTypesResponse;
|
|
24
|
+
async function askForEventsConfig(events, projectId, instanceId) {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
logger_1.logger.info(`\n${clc.bold("Enable Events")}: ${marked("If you enable events, you can write custom event handlers ([https://firebase.google.com/docs/extensions/install-extensions#eventarc](https://firebase.google.com/docs/extensions/install-extensions#eventarc)) that respond to these events.\n\nYou can always enable or disable events later. Events will be emitted via Eventarc. Fees apply ([https://cloud.google.com/eventarc/pricing](https://cloud.google.com/eventarc/pricing)).")}`);
|
|
27
|
+
if (!(await askShouldCollectEventsConfig())) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
let existingInstance;
|
|
31
|
+
try {
|
|
32
|
+
existingInstance = instanceId
|
|
33
|
+
? await extensionsApi.getInstance(projectId, instanceId)
|
|
34
|
+
: undefined;
|
|
35
|
+
}
|
|
36
|
+
catch (_c) {
|
|
37
|
+
}
|
|
38
|
+
const preselectedTypes = (_a = existingInstance === null || existingInstance === void 0 ? void 0 : existingInstance.config.allowedEventTypes) !== null && _a !== void 0 ? _a : [];
|
|
39
|
+
const oldLocation = (_b = existingInstance === null || existingInstance === void 0 ? void 0 : existingInstance.config.eventarcChannel) === null || _b === void 0 ? void 0 : _b.split("/")[3];
|
|
40
|
+
const location = await askForEventArcLocation(oldLocation);
|
|
41
|
+
const channel = `projects/${projectId}/locations/${location}/channels/firebase`;
|
|
42
|
+
const allowedEventTypes = await askForAllowedEventTypes(events, preselectedTypes);
|
|
43
|
+
return { channel, allowedEventTypes };
|
|
44
|
+
}
|
|
45
|
+
exports.askForEventsConfig = askForEventsConfig;
|
|
46
|
+
async function askForAllowedEventTypes(eventDescriptors, preselectedTypes) {
|
|
47
|
+
let valid = false;
|
|
48
|
+
let response = [];
|
|
49
|
+
const eventTypes = eventDescriptors.map((e, index) => ({
|
|
50
|
+
checked: false,
|
|
51
|
+
name: `${index + 1}. ${e.type}\n ${e.description}`,
|
|
52
|
+
value: e.type,
|
|
53
|
+
}));
|
|
54
|
+
while (!valid) {
|
|
55
|
+
response = await (0, prompt_1.promptOnce)({
|
|
56
|
+
name: "selectedEventTypesInput",
|
|
57
|
+
type: "checkbox",
|
|
58
|
+
default: preselectedTypes !== null && preselectedTypes !== void 0 ? preselectedTypes : [],
|
|
59
|
+
message: `Please select the events [${eventTypes.length} types total] that this extension is permitted to emit. ` +
|
|
60
|
+
"You can implement your own handlers that trigger when these events are emitted to customize the extension's behavior. ",
|
|
61
|
+
choices: eventTypes,
|
|
62
|
+
pageSize: 20,
|
|
63
|
+
});
|
|
64
|
+
valid = checkAllowedEventTypesResponse(response, eventDescriptors);
|
|
65
|
+
}
|
|
66
|
+
return response.filter((e) => e !== "");
|
|
67
|
+
}
|
|
68
|
+
exports.askForAllowedEventTypes = askForAllowedEventTypes;
|
|
69
|
+
async function askShouldCollectEventsConfig() {
|
|
70
|
+
return (0, prompt_1.promptOnce)({
|
|
71
|
+
type: "confirm",
|
|
72
|
+
name: "shouldCollectEvents",
|
|
73
|
+
message: `Would you like to enable events?`,
|
|
74
|
+
default: false,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
exports.askShouldCollectEventsConfig = askShouldCollectEventsConfig;
|
|
78
|
+
async function askForEventArcLocation(preselectedLocation) {
|
|
79
|
+
let valid = false;
|
|
80
|
+
const allowedRegions = ["us-central1", "us-west1", "europe-west4", "asia-northeast1"];
|
|
81
|
+
let location = "";
|
|
82
|
+
while (!valid) {
|
|
83
|
+
location = await (0, prompt_1.promptOnce)({
|
|
84
|
+
name: "input",
|
|
85
|
+
type: "list",
|
|
86
|
+
default: preselectedLocation !== null && preselectedLocation !== void 0 ? preselectedLocation : "us-central1",
|
|
87
|
+
message: "Which location would you like the Eventarc channel to live in? We recommend using the default option. A channel location that differs from the extension's Cloud Functions location can incur egress cost.",
|
|
88
|
+
choices: allowedRegions.map((e) => ({ checked: false, value: e })),
|
|
89
|
+
});
|
|
90
|
+
valid = allowedRegions.includes(location);
|
|
91
|
+
if (!valid) {
|
|
92
|
+
utils.logWarning(`Unexpected EventArc region '${location}' was specified. Allowed regions: ${allowedRegions.join(", ")}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return location;
|
|
96
|
+
}
|
|
97
|
+
exports.askForEventArcLocation = askForEventArcLocation;
|
package/lib/extensions/export.js
CHANGED
|
@@ -45,6 +45,7 @@ function displayExportInfo(withRef, withoutRef) {
|
|
|
45
45
|
}
|
|
46
46
|
exports.displayExportInfo = displayExportInfo;
|
|
47
47
|
function displaySpecs(specs) {
|
|
48
|
+
var _a;
|
|
48
49
|
for (let i = 0; i < specs.length; i++) {
|
|
49
50
|
const spec = specs[i];
|
|
50
51
|
logger_1.logger.info(`${i + 1}. ${(0, deploymentSummary_1.humanReadable)(spec)}`);
|
|
@@ -52,6 +53,12 @@ function displaySpecs(specs) {
|
|
|
52
53
|
for (const p of Object.entries(spec.params)) {
|
|
53
54
|
logger_1.logger.info(`\t${p[0]}=${p[1]}`);
|
|
54
55
|
}
|
|
56
|
+
if ((_a = spec.allowedEventTypes) === null || _a === void 0 ? void 0 : _a.length) {
|
|
57
|
+
logger_1.logger.info(`\tALLOWED_EVENTS=${spec.allowedEventTypes}`);
|
|
58
|
+
}
|
|
59
|
+
if (spec.eventarcChannel) {
|
|
60
|
+
logger_1.logger.info(`\tEVENTARC_CHANNEL=${spec.eventarcChannel}`);
|
|
61
|
+
}
|
|
55
62
|
logger_1.logger.info("");
|
|
56
63
|
}
|
|
57
64
|
}
|
|
@@ -58,6 +58,8 @@ async function createInstance(args) {
|
|
|
58
58
|
var _a, _b;
|
|
59
59
|
const config = {
|
|
60
60
|
params: args.params,
|
|
61
|
+
allowedEventTypes: args.allowedEventTypes,
|
|
62
|
+
eventarcChannel: args.eventarcChannel,
|
|
61
63
|
};
|
|
62
64
|
if (args.extensionSource && args.extensionVersionRef) {
|
|
63
65
|
throw new error_1.FirebaseError("ExtensionSource and ExtensionVersion both provided, but only one should be.");
|
|
@@ -73,6 +75,12 @@ async function createInstance(args) {
|
|
|
73
75
|
else {
|
|
74
76
|
throw new error_1.FirebaseError("No ExtensionVersion or ExtensionSource provided but one is required.");
|
|
75
77
|
}
|
|
78
|
+
if (args.allowedEventTypes) {
|
|
79
|
+
config.allowedEventTypes = args.allowedEventTypes;
|
|
80
|
+
}
|
|
81
|
+
if (args.eventarcChannel) {
|
|
82
|
+
config.eventarcChannel = args.eventarcChannel;
|
|
83
|
+
}
|
|
76
84
|
return createInstanceHelper(args.projectId, args.instanceId, config, args.validateOnly);
|
|
77
85
|
}
|
|
78
86
|
exports.createInstance = createInstance;
|
|
@@ -88,8 +96,16 @@ async function deleteInstance(projectId, instanceId) {
|
|
|
88
96
|
}
|
|
89
97
|
exports.deleteInstance = deleteInstance;
|
|
90
98
|
async function getInstance(projectId, instanceId) {
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
try {
|
|
100
|
+
const res = await apiClient.get(`/projects/${projectId}/instances/${instanceId}`);
|
|
101
|
+
return res.body;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (err.status === 404) {
|
|
105
|
+
throw new error_1.FirebaseError(`Extension instance '${clc.bold(instanceId)}' not found in project '${clc.bold(projectId)}'.`, { status: 404 });
|
|
106
|
+
}
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
93
109
|
}
|
|
94
110
|
exports.getInstance = getInstance;
|
|
95
111
|
async function listInstances(projectId) {
|
|
@@ -114,7 +130,7 @@ async function listInstances(projectId) {
|
|
|
114
130
|
exports.listInstances = listInstances;
|
|
115
131
|
async function configureInstance(args) {
|
|
116
132
|
var _a;
|
|
117
|
-
const
|
|
133
|
+
const reqBody = {
|
|
118
134
|
projectId: args.projectId,
|
|
119
135
|
instanceId: args.instanceId,
|
|
120
136
|
updateMask: "config.params",
|
|
@@ -124,8 +140,16 @@ async function configureInstance(args) {
|
|
|
124
140
|
params: args.params,
|
|
125
141
|
},
|
|
126
142
|
},
|
|
127
|
-
}
|
|
128
|
-
|
|
143
|
+
};
|
|
144
|
+
if (args.canEmitEvents) {
|
|
145
|
+
if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
|
|
146
|
+
throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
|
|
147
|
+
}
|
|
148
|
+
reqBody.data.config.allowedEventTypes = args.allowedEventTypes;
|
|
149
|
+
reqBody.data.config.eventarcChannel = args.eventarcChannel;
|
|
150
|
+
}
|
|
151
|
+
reqBody.updateMask += ",config.allowed_event_types,config.eventarc_channel";
|
|
152
|
+
return patchInstance(reqBody);
|
|
129
153
|
}
|
|
130
154
|
exports.configureInstance = configureInstance;
|
|
131
155
|
async function updateInstance(args) {
|
|
@@ -140,7 +164,15 @@ async function updateInstance(args) {
|
|
|
140
164
|
body.config.params = args.params;
|
|
141
165
|
updateMask += ",config.params";
|
|
142
166
|
}
|
|
143
|
-
|
|
167
|
+
if (args.canEmitEvents) {
|
|
168
|
+
if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
|
|
169
|
+
throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
|
|
170
|
+
}
|
|
171
|
+
body.config.allowedEventTypes = args.allowedEventTypes;
|
|
172
|
+
body.config.eventarcChannel = args.eventarcChannel;
|
|
173
|
+
}
|
|
174
|
+
updateMask += ",config.allowed_event_types,config.eventarc_channel";
|
|
175
|
+
return patchInstance({
|
|
144
176
|
projectId: args.projectId,
|
|
145
177
|
instanceId: args.instanceId,
|
|
146
178
|
updateMask,
|
|
@@ -163,7 +195,15 @@ async function updateInstanceFromRegistry(args) {
|
|
|
163
195
|
body.config.params = args.params;
|
|
164
196
|
updateMask += ",config.params";
|
|
165
197
|
}
|
|
166
|
-
|
|
198
|
+
if (args.canEmitEvents) {
|
|
199
|
+
if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
|
|
200
|
+
throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
|
|
201
|
+
}
|
|
202
|
+
body.config.allowedEventTypes = args.allowedEventTypes;
|
|
203
|
+
body.config.eventarcChannel = args.eventarcChannel;
|
|
204
|
+
}
|
|
205
|
+
updateMask += ",config.allowed_event_types,config.eventarc_channel";
|
|
206
|
+
return patchInstance({
|
|
167
207
|
projectId: args.projectId,
|
|
168
208
|
instanceId: args.instanceId,
|
|
169
209
|
updateMask,
|
|
@@ -48,7 +48,7 @@ async function writeLocalSecrets(specs, config, force) {
|
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
50
|
const writeBuffer = {};
|
|
51
|
-
const locallyOverridenSecretParams = extensionSpec.params.filter((p) => p.type === extensionsApi_1.ParamType.SECRET && spec.params[p.param].local);
|
|
51
|
+
const locallyOverridenSecretParams = extensionSpec.params.filter((p) => { var _a; return p.type === extensionsApi_1.ParamType.SECRET && ((_a = spec.params[p.param]) === null || _a === void 0 ? void 0 : _a.local); });
|
|
52
52
|
for (const paramSpec of locallyOverridenSecretParams) {
|
|
53
53
|
const key = paramSpec.param;
|
|
54
54
|
const localValue = spec.params[key].local;
|
|
@@ -11,6 +11,7 @@ const askUserForParam = require("./askUserForParam");
|
|
|
11
11
|
const track_1 = require("../track");
|
|
12
12
|
const env = require("../functions/env");
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
|
+
const warnings_1 = require("./warnings");
|
|
14
15
|
function getBaseParamBindings(params) {
|
|
15
16
|
let ret = {};
|
|
16
17
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -57,6 +58,7 @@ async function getParams(args) {
|
|
|
57
58
|
paramsMessage);
|
|
58
59
|
}
|
|
59
60
|
else if (args.paramsEnvPath) {
|
|
61
|
+
(0, warnings_1.paramsFlagDeprecationWarning)();
|
|
60
62
|
params = getParamsFromFile({
|
|
61
63
|
paramSpecs: args.paramSpecs,
|
|
62
64
|
paramsEnvPath: args.paramsEnvPath,
|
|
@@ -59,13 +59,16 @@ async function displayChanges(args) {
|
|
|
59
59
|
}
|
|
60
60
|
exports.displayChanges = displayChanges;
|
|
61
61
|
async function update(updateOptions) {
|
|
62
|
-
const { projectId, instanceId, source, extRef, params } = updateOptions;
|
|
62
|
+
const { projectId, instanceId, source, extRef, params, canEmitEvents, allowedEventTypes, eventarcChannel, } = updateOptions;
|
|
63
63
|
if (extRef) {
|
|
64
64
|
return await extensionsApi.updateInstanceFromRegistry({
|
|
65
65
|
projectId,
|
|
66
66
|
instanceId,
|
|
67
67
|
extRef,
|
|
68
68
|
params,
|
|
69
|
+
canEmitEvents,
|
|
70
|
+
allowedEventTypes,
|
|
71
|
+
eventarcChannel,
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
74
|
else if (source) {
|
|
@@ -74,6 +77,9 @@ async function update(updateOptions) {
|
|
|
74
77
|
instanceId,
|
|
75
78
|
extensionSource: source,
|
|
76
79
|
params,
|
|
80
|
+
canEmitEvents,
|
|
81
|
+
allowedEventTypes,
|
|
82
|
+
eventarcChannel,
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
85
|
throw new error_1.FirebaseError(`Neither a source nor a version of the extension was supplied for ${instanceId}. Please make sure this is a valid extension and try again.`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.displayWarningsForDeploy = exports.displayWarningPrompts = void 0;
|
|
3
|
+
exports.paramsFlagDeprecationWarning = exports.displayWarningsForDeploy = exports.displayWarningPrompts = void 0;
|
|
4
4
|
const { marked } = require("marked");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const extensionsApi_1 = require("./extensionsApi");
|
|
@@ -11,6 +11,7 @@ const deploymentSummary_1 = require("../deploy/extensions/deploymentSummary");
|
|
|
11
11
|
const planner_1 = require("../deploy/extensions/planner");
|
|
12
12
|
const functional_1 = require("../functional");
|
|
13
13
|
const utils = require("../utils");
|
|
14
|
+
const logger_1 = require("../logger");
|
|
14
15
|
function displayEAPWarning({ publisherId, sourceDownloadUri, githubLink, }) {
|
|
15
16
|
const publisherNameLink = githubLink ? `[${publisherId}](${githubLink})` : publisherId;
|
|
16
17
|
const warningMsg = `This extension is in preview and is built by a developer in the [Extensions Publisher Early Access Program](http://bit.ly/firex-provider). Its functionality might change in backward-incompatible ways. Since this extension isn't built by Firebase, reach out to ${publisherNameLink} with questions about this extension.`;
|
|
@@ -57,14 +58,20 @@ async function displayWarningsForDeploy(instancesToCreate) {
|
|
|
57
58
|
const experimental = nonEapExtensions.filter((i) => i.extension.registryLaunchStage === extensionsApi_1.RegistryLaunchStage.EXPERIMENTAL);
|
|
58
59
|
if (experimental.length) {
|
|
59
60
|
const humanReadableList = experimental.map((i) => `\t${(0, deploymentSummary_1.humanReadable)(i)}`).join("\n");
|
|
60
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`The following are instances of ${clc.bold("experimental")} extensions.They may not be production-ready. Their functionality may change in backward-incompatible ways before their official release, or they may be discontinued.\n${humanReadableList}\n
|
|
61
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`The following are instances of ${clc.bold("experimental")} extensions.They may not be production-ready. Their functionality may change in backward-incompatible ways before their official release, or they may be discontinued.\n${humanReadableList}\n`, { gfm: false }));
|
|
61
62
|
}
|
|
62
63
|
if (eapExtensions.length) {
|
|
63
64
|
const humanReadableList = eapExtensions.map(toListEntry).join("\n");
|
|
64
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`These extensions are in preview and are built by a developer in the Extensions Publisher Early Access Program (http://bit.ly/firex-provider. Their functionality might change in backwards-incompatible ways. Since these extensions aren't built by Firebase, reach out to their publisher with questions about them.` +
|
|
65
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`These extensions are in preview and are built by a developer in the Extensions Publisher Early Access Program (http://bit.ly/firex-provider). Their functionality might change in backwards-incompatible ways. Since these extensions aren't built by Firebase, reach out to their publisher with questions about them.` +
|
|
65
66
|
` They are provided “AS IS”, without any warranty, express or implied, from Google.` +
|
|
66
|
-
` Google disclaims all liability for any damages, direct or indirect, resulting from the use of these extensions\n${humanReadableList}
|
|
67
|
+
` Google disclaims all liability for any damages, direct or indirect, resulting from the use of these extensions\n${humanReadableList}`, { gfm: false }));
|
|
67
68
|
}
|
|
68
69
|
return experimental.length > 0 || eapExtensions.length > 0;
|
|
69
70
|
}
|
|
70
71
|
exports.displayWarningsForDeploy = displayWarningsForDeploy;
|
|
72
|
+
function paramsFlagDeprecationWarning() {
|
|
73
|
+
logger_1.logger.warn("The --params flag is deprecated and will be removed in firebase-tools@11. " +
|
|
74
|
+
"Instead, use an extensions manifest and `firebase deploy --only extensions` to deploy extensions noninteractively. " +
|
|
75
|
+
"See https://firebase.google.com/docs/extensions/manifest for more details");
|
|
76
|
+
}
|
|
77
|
+
exports.paramsFlagDeprecationWarning = paramsFlagDeprecationWarning;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.normalizeAndValidate = exports.validate = exports.normalize = exports.DEFAULT_CODEBASE = void 0;
|
|
3
|
+
exports.configForCodebase = exports.normalizeAndValidate = exports.validate = exports.normalize = exports.DEFAULT_CODEBASE = void 0;
|
|
4
4
|
const error_1 = require("../error");
|
|
5
5
|
exports.DEFAULT_CODEBASE = "default";
|
|
6
6
|
function normalize(config) {
|
|
@@ -40,16 +40,21 @@ function assertUnique(config, property) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
function validate(config) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
assertUnique([validated], "source");
|
|
48
|
-
assertUnique([validated], "codebase");
|
|
49
|
-
return [validated];
|
|
43
|
+
const validated = config.map((cfg) => validateSingle(cfg));
|
|
44
|
+
assertUnique(validated, "source");
|
|
45
|
+
assertUnique(validated, "codebase");
|
|
46
|
+
return validated;
|
|
50
47
|
}
|
|
51
48
|
exports.validate = validate;
|
|
52
49
|
function normalizeAndValidate(config) {
|
|
53
50
|
return validate(normalize(config));
|
|
54
51
|
}
|
|
55
52
|
exports.normalizeAndValidate = normalizeAndValidate;
|
|
53
|
+
function configForCodebase(config, codebase) {
|
|
54
|
+
const codebaseCfg = config.find((c) => c.codebase === codebase);
|
|
55
|
+
if (!codebaseCfg) {
|
|
56
|
+
throw new error_1.FirebaseError(`No functions config found for codebase ${codebase}`);
|
|
57
|
+
}
|
|
58
|
+
return codebaseCfg;
|
|
59
|
+
}
|
|
60
|
+
exports.configForCodebase = configForCodebase;
|
|
@@ -55,7 +55,7 @@ const actionFunction = async (options) => {
|
|
|
55
55
|
})
|
|
56
56
|
.then(() => {
|
|
57
57
|
const instance = serveFunctions.get();
|
|
58
|
-
const emulator = new shell.FunctionsEmulatorShell(instance
|
|
58
|
+
const emulator = new shell.FunctionsEmulatorShell(instance);
|
|
59
59
|
if (emulator.emulatedFunctions && emulator.emulatedFunctions.length === 0) {
|
|
60
60
|
logger_1.logger.info("No functions emulated.");
|
|
61
61
|
process.exit();
|
|
@@ -145,7 +145,7 @@ async function setInvokerUpdate(projectId, fnName, invoker) {
|
|
|
145
145
|
exports.setInvokerUpdate = setInvokerUpdate;
|
|
146
146
|
async function updateFunction(cloudFunction) {
|
|
147
147
|
const endpoint = `/${cloudFunction.name}`;
|
|
148
|
-
const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables");
|
|
148
|
+
const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables", "secretEnvironmentVariables");
|
|
149
149
|
try {
|
|
150
150
|
const headers = {};
|
|
151
151
|
if (previews_1.previews.artifactregistry) {
|
|
@@ -280,6 +280,7 @@ function endpointFromFunction(gcfFunction) {
|
|
|
280
280
|
}
|
|
281
281
|
exports.endpointFromFunction = endpointFromFunction;
|
|
282
282
|
function functionFromEndpoint(endpoint, sourceUploadUrl) {
|
|
283
|
+
var _a;
|
|
283
284
|
if (endpoint.platform !== "gcfv1") {
|
|
284
285
|
throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
|
|
285
286
|
}
|
|
@@ -334,7 +335,13 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
|
|
|
334
335
|
proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnector", "connector");
|
|
335
336
|
proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnectorEgressSettings", "egressSettings");
|
|
336
337
|
}
|
|
337
|
-
|
|
338
|
+
const codebase = endpoint.codebase || projectConfig.DEFAULT_CODEBASE;
|
|
339
|
+
if (codebase !== projectConfig.DEFAULT_CODEBASE) {
|
|
340
|
+
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.CODEBASE_LABEL]: codebase });
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
(_a = gcfFunction.labels) === null || _a === void 0 ? true : delete _a[exports.CODEBASE_LABEL];
|
|
344
|
+
}
|
|
338
345
|
return gcfFunction;
|
|
339
346
|
}
|
|
340
347
|
exports.functionFromEndpoint = functionFromEndpoint;
|