firebase-tools 10.5.0 → 10.6.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/functions-secrets-destroy.js +23 -3
- package/lib/commands/functions-secrets-prune.js +15 -12
- package/lib/commands/functions-secrets-set.js +51 -4
- package/lib/deploy/functions/backend.js +1 -5
- package/lib/deploy/functions/prepare.js +13 -3
- package/lib/deploy/functions/release/fabricator.js +1 -3
- package/lib/deploy/functions/release/index.js +21 -0
- package/lib/deploy/functions/release/planner.js +1 -2
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +11 -10
- package/lib/deploy/functions/runtimes/node/index.js +1 -1
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +5 -19
- package/lib/deploy/functions/services/storage.js +1 -6
- package/lib/emulator/extensions/postinstall.js +41 -0
- package/lib/emulator/functionsEmulatorShared.js +16 -20
- package/lib/emulator/storage/apis/firebase.js +6 -6
- package/lib/emulator/storage/apis/gcloud.js +9 -6
- package/lib/emulator/storage/files.js +6 -3
- package/lib/emulator/storage/index.js +9 -1
- package/lib/emulator/storage/metadata.js +18 -8
- package/lib/emulator/storage/rules/manager.js +7 -17
- package/lib/emulator/storage/server.js +38 -12
- package/lib/extensions/askUserForParam.js +14 -11
- package/lib/extensions/emulator/optionsHelper.js +5 -7
- package/lib/extensions/emulator/triggerHelper.js +11 -14
- package/lib/extensions/extensionsApi.js +2 -1
- package/lib/extensions/manifest.js +1 -1
- package/lib/extensions/paramHelper.js +16 -10
- package/lib/functions/env.js +10 -2
- package/lib/functions/runtimeConfigExport.js +10 -6
- package/lib/functions/secrets.js +99 -6
- package/lib/gcp/cloudfunctions.js +6 -13
- package/lib/gcp/cloudfunctionsv2.js +14 -23
- package/lib/gcp/cloudtasks.js +5 -3
- package/lib/gcp/secretManager.js +1 -1
- package/lib/utils.js +30 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -155,6 +155,7 @@ class StorageLayer {
|
|
|
155
155
|
cacheControl: upload.metadata.cacheControl,
|
|
156
156
|
customMetadata: upload.metadata.metadata,
|
|
157
157
|
}, this._cloudFunctions, this._persistence.readBytes(upload.path, upload.size));
|
|
158
|
+
metadata.update(upload.metadata, false);
|
|
158
159
|
const authorized = await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), upload.bucketId, types_1.RulesetOperationMethod.CREATE, { after: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, upload.authorization);
|
|
159
160
|
if (!authorized) {
|
|
160
161
|
this._persistence.deleteFile(upload.path);
|
|
@@ -347,10 +348,12 @@ class StorageLayer {
|
|
|
347
348
|
logger_1.logger.warn(`Could not find file "${blobPath}" in storage export.`);
|
|
348
349
|
continue;
|
|
349
350
|
}
|
|
350
|
-
const
|
|
351
|
-
this.
|
|
351
|
+
const decodedBlobPath = decodeURIComponent(blobPath);
|
|
352
|
+
const blobDiskPath = this._persistence.getDiskPath(decodedBlobPath);
|
|
353
|
+
const file = new StoredFile(metadata, blobDiskPath);
|
|
354
|
+
this._files.set(decodedBlobPath, file);
|
|
355
|
+
fse.copyFileSync(blobAbsPath, blobDiskPath);
|
|
352
356
|
}
|
|
353
|
-
fse.copySync(blobsDir, this.dirPath);
|
|
354
357
|
}
|
|
355
358
|
*walkDirSync(dir) {
|
|
356
359
|
const files = (0, fs_1.readdirSync)(dir);
|
|
@@ -21,7 +21,7 @@ class StorageEmulator {
|
|
|
21
21
|
this._files = new Map();
|
|
22
22
|
this._buckets = new Map();
|
|
23
23
|
this._rulesRuntime = new runtime_1.StorageRulesRuntime();
|
|
24
|
-
this._rulesManager =
|
|
24
|
+
this._rulesManager = this.createRulesManager(this.args.rules);
|
|
25
25
|
this._cloudFunctions = new cloudFunctions_1.StorageCloudFunctions(args.projectId);
|
|
26
26
|
this._persistence = new persistence_1.Persistence(this.getPersistenceTmpDir());
|
|
27
27
|
this._uploadService = new upload_1.UploadService(this._persistence);
|
|
@@ -82,6 +82,14 @@ class StorageEmulator {
|
|
|
82
82
|
getApp() {
|
|
83
83
|
return this._app;
|
|
84
84
|
}
|
|
85
|
+
async replaceRules(rules) {
|
|
86
|
+
await this._rulesManager.stop();
|
|
87
|
+
this._rulesManager = this.createRulesManager(rules);
|
|
88
|
+
return this._rulesManager.start();
|
|
89
|
+
}
|
|
90
|
+
createRulesManager(rules) {
|
|
91
|
+
return (0, manager_1.createStorageRulesManager)(rules, this._rulesRuntime);
|
|
92
|
+
}
|
|
85
93
|
getPersistenceTmpDir() {
|
|
86
94
|
return `${(0, os_1.tmpdir)()}/firebase/storage/blobs`;
|
|
87
95
|
}
|
|
@@ -44,7 +44,7 @@ class StoredFileMetadata {
|
|
|
44
44
|
throw new Error("Must pass bytes array or opts object with size, md5hash, and crc32c");
|
|
45
45
|
}
|
|
46
46
|
if (incomingMetadata) {
|
|
47
|
-
this.update(incomingMetadata);
|
|
47
|
+
this.update(incomingMetadata, false);
|
|
48
48
|
}
|
|
49
49
|
this.deleteFieldsSetAsNull();
|
|
50
50
|
this.setDownloadTokensFromCustomMetadata();
|
|
@@ -86,8 +86,10 @@ class StoredFileMetadata {
|
|
|
86
86
|
}
|
|
87
87
|
if (this.customMetadata.firebaseStorageDownloadTokens) {
|
|
88
88
|
this.downloadTokens = [
|
|
89
|
-
...
|
|
90
|
-
|
|
89
|
+
...new Set([
|
|
90
|
+
...this.downloadTokens,
|
|
91
|
+
...this.customMetadata.firebaseStorageDownloadTokens.split(","),
|
|
92
|
+
]),
|
|
91
93
|
];
|
|
92
94
|
delete this.customMetadata.firebaseStorageDownloadTokens;
|
|
93
95
|
}
|
|
@@ -115,7 +117,7 @@ class StoredFileMetadata {
|
|
|
115
117
|
});
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
|
-
update(incoming) {
|
|
120
|
+
update(incoming, shouldTrigger = true) {
|
|
119
121
|
if (incoming.contentDisposition) {
|
|
120
122
|
this.contentDisposition = incoming.contentDisposition;
|
|
121
123
|
}
|
|
@@ -143,15 +145,17 @@ class StoredFileMetadata {
|
|
|
143
145
|
}
|
|
144
146
|
this.setDownloadTokensFromCustomMetadata();
|
|
145
147
|
this.deleteFieldsSetAsNull();
|
|
146
|
-
|
|
148
|
+
if (shouldTrigger) {
|
|
149
|
+
this._cloudFunctions.dispatch("metadataUpdate", new CloudStorageObjectMetadata(this));
|
|
150
|
+
}
|
|
147
151
|
}
|
|
148
|
-
addDownloadToken() {
|
|
152
|
+
addDownloadToken(shouldTrigger = true) {
|
|
149
153
|
if (!this.downloadTokens.length) {
|
|
150
154
|
this.downloadTokens.push(uuid.v4());
|
|
151
155
|
return;
|
|
152
156
|
}
|
|
153
157
|
this.downloadTokens = [...this.downloadTokens, uuid.v4()];
|
|
154
|
-
this.update({});
|
|
158
|
+
this.update({}, shouldTrigger);
|
|
155
159
|
}
|
|
156
160
|
deleteDownloadToken(token) {
|
|
157
161
|
if (!this.downloadTokens.length) {
|
|
@@ -160,7 +164,7 @@ class StoredFileMetadata {
|
|
|
160
164
|
const remainingTokens = this.downloadTokens.filter((t) => t !== token);
|
|
161
165
|
this.downloadTokens = remainingTokens;
|
|
162
166
|
if (remainingTokens.length === 0) {
|
|
163
|
-
this.addDownloadToken();
|
|
167
|
+
this.addDownloadToken(false);
|
|
164
168
|
}
|
|
165
169
|
this.update({});
|
|
166
170
|
}
|
|
@@ -265,6 +269,12 @@ class CloudStorageObjectMetadata {
|
|
|
265
269
|
if (metadata.cacheControl) {
|
|
266
270
|
this.cacheControl = metadata.cacheControl;
|
|
267
271
|
}
|
|
272
|
+
if (metadata.contentDisposition) {
|
|
273
|
+
this.contentDisposition = metadata.contentDisposition;
|
|
274
|
+
}
|
|
275
|
+
if (metadata.contentEncoding) {
|
|
276
|
+
this.contentEncoding = metadata.contentEncoding;
|
|
277
|
+
}
|
|
268
278
|
if (metadata.customTime) {
|
|
269
279
|
this.customTime = toSerializedDate(metadata.customTime);
|
|
270
280
|
}
|
|
@@ -5,6 +5,7 @@ const chokidar = require("chokidar");
|
|
|
5
5
|
const emulatorLogger_1 = require("../../emulatorLogger");
|
|
6
6
|
const types_1 = require("../../types");
|
|
7
7
|
const runtime_1 = require("./runtime");
|
|
8
|
+
const fsutils_1 = require("../../../fsutils");
|
|
8
9
|
function createStorageRulesManager(rules, runtime) {
|
|
9
10
|
return Array.isArray(rules)
|
|
10
11
|
? new ResourceBasedStorageRulesManager(rules, runtime)
|
|
@@ -18,31 +19,24 @@ class DefaultStorageRulesManager {
|
|
|
18
19
|
this._logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE);
|
|
19
20
|
this._rules = _rules;
|
|
20
21
|
}
|
|
21
|
-
start() {
|
|
22
|
-
|
|
22
|
+
async start() {
|
|
23
|
+
const issues = await this.loadRuleset();
|
|
24
|
+
this.updateWatcher(this._rules.name);
|
|
25
|
+
return issues;
|
|
23
26
|
}
|
|
24
27
|
getRuleset() {
|
|
25
28
|
return this._ruleset;
|
|
26
29
|
}
|
|
27
|
-
async updateSourceFile(rules) {
|
|
28
|
-
const prevRulesFile = this._rules.name;
|
|
29
|
-
this._rules = rules;
|
|
30
|
-
const issues = await this.loadRuleset();
|
|
31
|
-
this.updateWatcher(rules.name, prevRulesFile);
|
|
32
|
-
return issues;
|
|
33
|
-
}
|
|
34
30
|
async stop() {
|
|
35
31
|
await this._watcher.close();
|
|
36
32
|
}
|
|
37
|
-
updateWatcher(rulesFile
|
|
38
|
-
if (prevRulesFile) {
|
|
39
|
-
this._watcher.unwatch(prevRulesFile);
|
|
40
|
-
}
|
|
33
|
+
updateWatcher(rulesFile) {
|
|
41
34
|
this._watcher = chokidar
|
|
42
35
|
.watch(rulesFile, { persistent: true, ignoreInitial: true })
|
|
43
36
|
.on("change", async () => {
|
|
44
37
|
await new Promise((res) => setTimeout(res, 5));
|
|
45
38
|
this._logger.logLabeled("BULLET", "storage", "Change detected, updating rules for Cloud Storage...");
|
|
39
|
+
this._rules.content = (0, fsutils_1.readFile)(rulesFile);
|
|
46
40
|
await this.loadRuleset();
|
|
47
41
|
});
|
|
48
42
|
}
|
|
@@ -83,10 +77,6 @@ class ResourceBasedStorageRulesManager {
|
|
|
83
77
|
var _a;
|
|
84
78
|
return (_a = this._rulesManagers.get(resource)) === null || _a === void 0 ? void 0 : _a.getRuleset();
|
|
85
79
|
}
|
|
86
|
-
updateSourceFile(rules, resource) {
|
|
87
|
-
const rulesManager = this._rulesManagers.get(resource) || this.createRulesManager(resource, rules);
|
|
88
|
-
return rulesManager.updateSourceFile(rules);
|
|
89
|
-
}
|
|
90
80
|
async stop() {
|
|
91
81
|
await Promise.all(Array.from(this._rulesManagers.values(), async (rulesManager) => await rulesManager.stop()));
|
|
92
82
|
}
|
|
@@ -8,6 +8,7 @@ const types_1 = require("../types");
|
|
|
8
8
|
const bodyParser = require("body-parser");
|
|
9
9
|
const gcloud_1 = require("./apis/gcloud");
|
|
10
10
|
const firebase_1 = require("./apis/firebase");
|
|
11
|
+
const errors_1 = require("../auth/errors");
|
|
11
12
|
function createApp(defaultProjectId, emulator) {
|
|
12
13
|
const { storageLayer } = emulator;
|
|
13
14
|
const app = express();
|
|
@@ -44,21 +45,43 @@ function createApp(defaultProjectId, emulator) {
|
|
|
44
45
|
res.sendStatus(200);
|
|
45
46
|
});
|
|
46
47
|
app.put("/internal/setRules", async (req, res) => {
|
|
47
|
-
const
|
|
48
|
-
if (!(
|
|
49
|
-
res.status(400).
|
|
48
|
+
const rulesRaw = req.body.rules;
|
|
49
|
+
if (!(rulesRaw && Array.isArray(rulesRaw.files) && rulesRaw.files.length > 0)) {
|
|
50
|
+
res.status(400).json({
|
|
51
|
+
message: "Request body must include 'rules.files' array",
|
|
52
|
+
});
|
|
50
53
|
return;
|
|
51
54
|
}
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const { files } = rulesRaw;
|
|
56
|
+
function parseRulesFromFiles(files) {
|
|
57
|
+
if (files.length === 1) {
|
|
58
|
+
const file = files[0];
|
|
59
|
+
if (!isRulesFile(file)) {
|
|
60
|
+
throw new errors_1.InvalidArgumentError("Each member of 'rules.files' array must contain 'name' and 'content'");
|
|
61
|
+
}
|
|
62
|
+
return { name: file.name, content: file.content };
|
|
63
|
+
}
|
|
64
|
+
const rules = [];
|
|
65
|
+
for (const file of files) {
|
|
66
|
+
if (!isRulesFile(file) || !file.resource) {
|
|
67
|
+
throw new errors_1.InvalidArgumentError("Each member of 'rules.files' array must contain 'name', 'content', and 'resource'");
|
|
68
|
+
}
|
|
69
|
+
rules.push({ resource: file.resource, rules: { name: file.name, content: file.content } });
|
|
70
|
+
}
|
|
71
|
+
return rules;
|
|
58
72
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
let rules;
|
|
74
|
+
try {
|
|
75
|
+
rules = parseRulesFromFiles(files);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (err instanceof errors_1.InvalidArgumentError) {
|
|
79
|
+
res.status(400).json({ message: err.message });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
const issues = await emulator.replaceRules(rules);
|
|
62
85
|
if (issues.errors.length > 0) {
|
|
63
86
|
res.status(400).json({
|
|
64
87
|
message: "There was an error updating rules, see logs for more details",
|
|
@@ -78,3 +101,6 @@ function createApp(defaultProjectId, emulator) {
|
|
|
78
101
|
return Promise.resolve(app);
|
|
79
102
|
}
|
|
80
103
|
exports.createApp = createApp;
|
|
104
|
+
function isRulesFile(file) {
|
|
105
|
+
return (typeof file.name === "string" && typeof file.content === "string");
|
|
106
|
+
}
|
|
@@ -172,12 +172,12 @@ async function promptSecretLocations(paramSpec) {
|
|
|
172
172
|
choices: [
|
|
173
173
|
{
|
|
174
174
|
checked: true,
|
|
175
|
-
name: "Google Cloud Secret Manager",
|
|
175
|
+
name: "Google Cloud Secret Manager (Used by deployed extensions and emulator)",
|
|
176
176
|
value: SecretLocation.CLOUD.toString(),
|
|
177
177
|
},
|
|
178
178
|
{
|
|
179
179
|
checked: false,
|
|
180
|
-
name: "Local file (
|
|
180
|
+
name: "Local file (Used by emulator only)",
|
|
181
181
|
value: SecretLocation.LOCAL.toString(),
|
|
182
182
|
},
|
|
183
183
|
],
|
|
@@ -191,25 +191,28 @@ async function promptSecretLocations(paramSpec) {
|
|
|
191
191
|
choices: [
|
|
192
192
|
{
|
|
193
193
|
checked: false,
|
|
194
|
-
name: "Google Cloud Secret Manager",
|
|
194
|
+
name: "Google Cloud Secret Manager (Used by deployed extensions and emulator)",
|
|
195
195
|
value: SecretLocation.CLOUD.toString(),
|
|
196
196
|
},
|
|
197
197
|
{
|
|
198
198
|
checked: false,
|
|
199
|
-
name: "Local file (
|
|
199
|
+
name: "Local file (Used by emulator only)",
|
|
200
200
|
value: SecretLocation.LOCAL.toString(),
|
|
201
201
|
},
|
|
202
202
|
],
|
|
203
203
|
});
|
|
204
204
|
}
|
|
205
205
|
async function promptLocalSecret(instanceId, paramSpec) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
206
|
+
let value;
|
|
207
|
+
do {
|
|
208
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, "Configure a local secret value for Extensions Emulator");
|
|
209
|
+
value = await (0, prompt_1.promptOnce)({
|
|
210
|
+
name: paramSpec.param,
|
|
211
|
+
type: "input",
|
|
212
|
+
message: `This secret will be stored in ./extensions/${instanceId}.secret.local.\n` +
|
|
213
|
+
`Enter value for "${paramSpec.label.trim()}" to be used by Extensions Emulator:`,
|
|
214
|
+
});
|
|
215
|
+
} while (!value);
|
|
213
216
|
return value;
|
|
214
217
|
}
|
|
215
218
|
async function promptReconfigureSecret(projectId, instanceId, paramSpec) {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getParams = exports.getSecretEnvVars = exports.getNonSecretEnv = exports.getExtensionFunctionInfo = exports.buildOptions = void 0;
|
|
4
4
|
const fs = require("fs-extra");
|
|
5
|
-
const _ = require("lodash");
|
|
6
5
|
const path = require("path");
|
|
7
6
|
const paramHelper = require("../paramHelper");
|
|
8
7
|
const specHelper = require("./specHelper");
|
|
@@ -159,12 +158,10 @@ function buildConfig(functionResources, testConfig) {
|
|
|
159
158
|
return config;
|
|
160
159
|
}
|
|
161
160
|
function getFunctionSourceDirectory(functionResources) {
|
|
161
|
+
var _a;
|
|
162
162
|
let sourceDirectory;
|
|
163
163
|
for (const r of functionResources) {
|
|
164
|
-
|
|
165
|
-
if (!dir) {
|
|
166
|
-
dir = "functions";
|
|
167
|
-
}
|
|
164
|
+
const dir = ((_a = r.properties) === null || _a === void 0 ? void 0 : _a.sourceDirectory) || "functions";
|
|
168
165
|
if (!sourceDirectory) {
|
|
169
166
|
sourceDirectory = dir;
|
|
170
167
|
}
|
|
@@ -172,14 +169,15 @@ function getFunctionSourceDirectory(functionResources) {
|
|
|
172
169
|
throw new error_1.FirebaseError(`Found function resources with different sourceDirectories: '${sourceDirectory}' and '${dir}'. The extensions emulator only supports a single sourceDirectory.`);
|
|
173
170
|
}
|
|
174
171
|
}
|
|
175
|
-
return sourceDirectory;
|
|
172
|
+
return sourceDirectory || "functions";
|
|
176
173
|
}
|
|
177
174
|
function shouldEmulateFunctions(resources) {
|
|
178
175
|
return resources.length > 0;
|
|
179
176
|
}
|
|
180
177
|
function shouldEmulate(emulatorName, resources) {
|
|
178
|
+
var _a, _b;
|
|
181
179
|
for (const r of resources) {
|
|
182
|
-
const eventType =
|
|
180
|
+
const eventType = ((_b = (_a = r.properties) === null || _a === void 0 ? void 0 : _a.eventTrigger) === null || _b === void 0 ? void 0 : _b.eventType) || "";
|
|
183
181
|
if (eventType.includes(emulatorName)) {
|
|
184
182
|
return true;
|
|
185
183
|
}
|
|
@@ -1,32 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.functionResourceToEmulatedTriggerDefintion = void 0;
|
|
4
|
-
const _ = require("lodash");
|
|
5
4
|
const functionsEmulatorShared_1 = require("../../emulator/functionsEmulatorShared");
|
|
6
5
|
const emulatorLogger_1 = require("../../emulator/emulatorLogger");
|
|
7
6
|
const types_1 = require("../../emulator/types");
|
|
7
|
+
const proto = require("../../gcp/proto");
|
|
8
8
|
function functionResourceToEmulatedTriggerDefintion(resource) {
|
|
9
9
|
const etd = {
|
|
10
10
|
name: resource.name,
|
|
11
11
|
entryPoint: resource.name,
|
|
12
12
|
platform: "gcfv1",
|
|
13
13
|
};
|
|
14
|
-
const properties =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (properties.location) {
|
|
19
|
-
etd.regions = [properties.location];
|
|
20
|
-
}
|
|
21
|
-
if (properties.availableMemoryMb) {
|
|
22
|
-
etd.availableMemoryMb = properties.availableMemoryMb;
|
|
23
|
-
}
|
|
14
|
+
const properties = resource.properties || {};
|
|
15
|
+
proto.renameIfPresent(etd, properties, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
16
|
+
proto.renameIfPresent(etd, properties, "regions", "location", (str) => [str]);
|
|
17
|
+
proto.copyIfPresent(etd, properties, "availableMemoryMb");
|
|
24
18
|
if (properties.httpsTrigger) {
|
|
25
19
|
etd.httpsTrigger = properties.httpsTrigger;
|
|
26
20
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
if (properties.eventTrigger) {
|
|
22
|
+
etd.eventTrigger = {
|
|
23
|
+
eventType: properties.eventTrigger.eventType,
|
|
24
|
+
resource: properties.eventTrigger.resource,
|
|
25
|
+
service: (0, functionsEmulatorShared_1.getServiceFromEventType)(properties.eventTrigger.eventType),
|
|
26
|
+
};
|
|
30
27
|
}
|
|
31
28
|
else {
|
|
32
29
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).log("WARN", `Function '${resource.name} is missing a trigger in extension.yaml. Please add one, as triggers defined in code are ignored.`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getExtension = exports.deleteExtension = exports.unpublishExtension = exports.publishExtensionVersion = exports.undeprecateExtensionVersion = exports.deprecateExtensionVersion = exports.registerPublisherProfile = exports.getPublisherProfile = exports.listExtensionVersions = exports.listExtensions = exports.getExtensionVersion = exports.getSource = exports.createSource = exports.updateInstanceFromRegistry = exports.updateInstance = exports.configureInstance = exports.listInstances = exports.getInstance = exports.deleteInstance = exports.createInstance = exports.ParamType = exports.Visibility = exports.RegistryLaunchStage = void 0;
|
|
3
|
+
exports.getExtension = exports.deleteExtension = exports.unpublishExtension = exports.publishExtensionVersion = exports.undeprecateExtensionVersion = exports.deprecateExtensionVersion = exports.registerPublisherProfile = exports.getPublisherProfile = exports.listExtensionVersions = exports.listExtensions = exports.getExtensionVersion = exports.getSource = exports.createSource = exports.updateInstanceFromRegistry = exports.updateInstance = exports.configureInstance = exports.listInstances = exports.getInstance = exports.deleteInstance = exports.createInstance = exports.ParamType = exports.FUNCTIONS_RESOURCE_TYPE = exports.Visibility = exports.RegistryLaunchStage = void 0;
|
|
4
4
|
const yaml = require("js-yaml");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const { marked } = require("marked");
|
|
@@ -26,6 +26,7 @@ var Visibility;
|
|
|
26
26
|
Visibility["UNLISTED"] = "unlisted";
|
|
27
27
|
Visibility["PUBLIC"] = "public";
|
|
28
28
|
})(Visibility = exports.Visibility || (exports.Visibility = {}));
|
|
29
|
+
exports.FUNCTIONS_RESOURCE_TYPE = "firebaseextensions.v1beta.function";
|
|
29
30
|
var ParamType;
|
|
30
31
|
(function (ParamType) {
|
|
31
32
|
ParamType["STRING"] = "STRING";
|
|
@@ -170,7 +170,7 @@ function showDeprecationWarning() {
|
|
|
170
170
|
}
|
|
171
171
|
exports.showDeprecationWarning = showDeprecationWarning;
|
|
172
172
|
function showPreviewWarning() {
|
|
173
|
-
utils.logLabeledWarning(extensionsHelper_1.logPrefix,
|
|
173
|
+
utils.logLabeledWarning(extensionsHelper_1.logPrefix, `See these changes in your Firebase Emulator by running "firebase emulators:start". ` +
|
|
174
174
|
`Run ${clc.bold("firebase deploy (--only extensions)")} to deploy the changes to your Firebase project. `);
|
|
175
175
|
}
|
|
176
176
|
exports.showPreviewWarning = showPreviewWarning;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = exports.buildBindingOptionsWithBaseValue = exports.getBaseParamBindings = void 0;
|
|
4
|
-
const _ = require("lodash");
|
|
5
4
|
const path = require("path");
|
|
6
5
|
const clc = require("cli-color");
|
|
7
6
|
const fs = require("fs-extra");
|
|
@@ -11,6 +10,7 @@ const extensionsHelper_1 = require("./extensionsHelper");
|
|
|
11
10
|
const askUserForParam = require("./askUserForParam");
|
|
12
11
|
const track = require("../track");
|
|
13
12
|
const env = require("../functions/env");
|
|
13
|
+
const utils_1 = require("../utils");
|
|
14
14
|
function getBaseParamBindings(params) {
|
|
15
15
|
let ret = {};
|
|
16
16
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -37,8 +37,9 @@ function setNewDefaults(params, newDefaults) {
|
|
|
37
37
|
}
|
|
38
38
|
exports.setNewDefaults = setNewDefaults;
|
|
39
39
|
function getParamsWithCurrentValuesAsDefaults(extensionInstance) {
|
|
40
|
-
|
|
41
|
-
const
|
|
40
|
+
var _a, _b, _c, _d;
|
|
41
|
+
const specParams = (0, utils_1.cloneDeep)(((_c = (_b = (_a = extensionInstance === null || extensionInstance === void 0 ? void 0 : extensionInstance.config) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.spec) === null || _c === void 0 ? void 0 : _c.params) || []);
|
|
42
|
+
const currentParams = (0, utils_1.cloneDeep)(((_d = extensionInstance === null || extensionInstance === void 0 ? void 0 : extensionInstance.config) === null || _d === void 0 ? void 0 : _d.params) || {});
|
|
42
43
|
return setNewDefaults(specParams, currentParams);
|
|
43
44
|
}
|
|
44
45
|
exports.getParamsWithCurrentValuesAsDefaults = getParamsWithCurrentValuesAsDefaults;
|
|
@@ -71,7 +72,8 @@ async function getParams(args) {
|
|
|
71
72
|
reconfiguring: !!args.reconfiguring,
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
const paramNames = Object.keys(params);
|
|
76
|
+
void track("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
|
|
75
77
|
return params;
|
|
76
78
|
}
|
|
77
79
|
exports.getParams = getParams;
|
|
@@ -103,27 +105,31 @@ async function getParamsForUpdate(args) {
|
|
|
103
105
|
instanceId: args.instanceId,
|
|
104
106
|
});
|
|
105
107
|
}
|
|
106
|
-
|
|
108
|
+
const paramNames = Object.keys(params);
|
|
109
|
+
void track("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
|
|
107
110
|
return params;
|
|
108
111
|
}
|
|
109
112
|
exports.getParamsForUpdate = getParamsForUpdate;
|
|
110
113
|
async function promptForNewParams(args) {
|
|
111
114
|
const newParamBindingOptions = buildBindingOptionsWithBaseValue(args.currentParams);
|
|
112
115
|
const firebaseProjectParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId);
|
|
113
|
-
const
|
|
116
|
+
const sameParam = (param1) => (param2) => {
|
|
114
117
|
return param1.type === param2.type && param1.param === param2.param;
|
|
115
118
|
};
|
|
119
|
+
const paramDiff = (left, right) => {
|
|
120
|
+
return left.filter((aLeft) => !right.find(sameParam(aLeft)));
|
|
121
|
+
};
|
|
116
122
|
const oldParams = args.spec.params.filter((p) => Object.keys(args.currentParams).includes(p.param));
|
|
117
|
-
let paramsDiffDeletions =
|
|
123
|
+
let paramsDiffDeletions = paramDiff(oldParams, args.newSpec.params);
|
|
118
124
|
paramsDiffDeletions = (0, extensionsHelper_1.substituteParams)(paramsDiffDeletions, firebaseProjectParams);
|
|
119
|
-
let paramsDiffAdditions =
|
|
125
|
+
let paramsDiffAdditions = paramDiff(args.newSpec.params, oldParams);
|
|
120
126
|
paramsDiffAdditions = (0, extensionsHelper_1.substituteParams)(paramsDiffAdditions, firebaseProjectParams);
|
|
121
127
|
if (paramsDiffDeletions.length) {
|
|
122
128
|
logger_1.logger.info("The following params will no longer be used:");
|
|
123
|
-
|
|
129
|
+
for (const param of paramsDiffDeletions) {
|
|
124
130
|
logger_1.logger.info(clc.red(`- ${param.param}: ${args.currentParams[param.param.toUpperCase()]}`));
|
|
125
131
|
delete newParamBindingOptions[param.param.toUpperCase()];
|
|
126
|
-
}
|
|
132
|
+
}
|
|
127
133
|
}
|
|
128
134
|
if (paramsDiffAdditions.length) {
|
|
129
135
|
logger_1.logger.info("To update this instance, configure the following new parameters:");
|
package/lib/functions/env.js
CHANGED
|
@@ -42,6 +42,15 @@ const LINE_RE = new RegExp("^" +
|
|
|
42
42
|
"\\s*" +
|
|
43
43
|
"(?:#[^\\n]*)?" +
|
|
44
44
|
"$", "gms");
|
|
45
|
+
const ESCAPE_SEQUENCES_TO_CHARACTERS = {
|
|
46
|
+
"\\n": "\n",
|
|
47
|
+
"\\r": "\r",
|
|
48
|
+
"\\t": "\t",
|
|
49
|
+
"\\v": "\v",
|
|
50
|
+
"\\\\": "\\",
|
|
51
|
+
"\\'": "'",
|
|
52
|
+
'\\"': '"',
|
|
53
|
+
};
|
|
45
54
|
function parse(data) {
|
|
46
55
|
const envs = {};
|
|
47
56
|
const errors = [];
|
|
@@ -54,8 +63,7 @@ function parse(data) {
|
|
|
54
63
|
if ((quotesMatch = /^(["'])(.*)\1$/ms.exec(v)) != null) {
|
|
55
64
|
v = quotesMatch[2];
|
|
56
65
|
if (quotesMatch[1] === '"') {
|
|
57
|
-
v = v.replace(
|
|
58
|
-
v = v.replace(/\\([\\'"])/g, "$1");
|
|
66
|
+
v = v.replace(/\\[nrtv\\'"]/g, (match) => ESCAPE_SEQUENCES_TO_CHARACTERS[match]);
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
envs[k] = v;
|
|
@@ -115,13 +115,17 @@ function hydrateEnvs(pInfos, prefix) {
|
|
|
115
115
|
return errMsg;
|
|
116
116
|
}
|
|
117
117
|
exports.hydrateEnvs = hydrateEnvs;
|
|
118
|
+
const CHARACTERS_TO_ESCAPE_SEQUENCES = {
|
|
119
|
+
"\n": "\\n",
|
|
120
|
+
"\r": "\\r",
|
|
121
|
+
"\t": "\\t",
|
|
122
|
+
"\v": "\\v",
|
|
123
|
+
"\\": "\\\\",
|
|
124
|
+
'"': '\\"',
|
|
125
|
+
"'": "\\'",
|
|
126
|
+
};
|
|
118
127
|
function escape(s) {
|
|
119
|
-
|
|
120
|
-
.replace("\n", "\\n")
|
|
121
|
-
.replace("\r", "\\r")
|
|
122
|
-
.replace("\t", "\\t")
|
|
123
|
-
.replace("\v", "\\v");
|
|
124
|
-
return result.replace(/(['"])/g, "\\$1");
|
|
128
|
+
return s.replace(/[\n\r\t\v\\"']/g, (ch) => CHARACTERS_TO_ESCAPE_SEQUENCES[ch]);
|
|
125
129
|
}
|
|
126
130
|
function toDotenvFormat(envs, header = "") {
|
|
127
131
|
const lines = envs.map(({ newKey, value }) => `${newKey}="${escape(value)}"`);
|