firebase-tools 10.1.2 → 10.1.3
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/apiv2.js +91 -48
- package/lib/archiveDirectory.js +63 -73
- package/lib/commands/ext-dev-usage.js +3 -8
- package/lib/database/metadata.js +16 -24
- package/lib/deploy/functions/backend.js +3 -1
- package/lib/deploy/functions/prepare.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +4 -1
- package/lib/deploy/functions/validate.js +28 -1
- package/lib/deploy/hosting/convertConfig.js +45 -24
- package/lib/deploy/hosting/prepare.js +1 -1
- package/lib/emulator/functionsEmulator.js +3 -1
- package/lib/extensions/extensionsApi.js +0 -1
- package/lib/gcp/cloudbilling.js +8 -19
- package/lib/gcp/cloudfunctions.js +22 -46
- package/lib/gcp/cloudlogging.js +8 -11
- package/lib/gcp/cloudmonitoring.js +8 -5
- package/lib/gcp/cloudscheduler.js +7 -18
- package/lib/gcp/firedata.js +5 -4
- package/lib/gcp/firestore.js +5 -5
- package/lib/gcp/iam.js +18 -33
- package/lib/gcp/resourceManager.js +8 -13
- package/lib/gcp/runtimeconfig.js +31 -53
- package/lib/gcp/secretManager.js +21 -43
- package/lib/gcp/storage.js +24 -29
- package/npm-shrinkwrap.json +965 -970
- package/package.json +4 -2
- package/schema/firebase-config.json +387 -12
- package/templates/init/hosting/index.html +1 -1
package/lib/apiv2.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Client = exports.setAccessToken = exports.setRefreshToken = void 0;
|
|
4
|
-
const stream_1 = require("stream");
|
|
5
4
|
const url_1 = require("url");
|
|
5
|
+
const stream_1 = require("stream");
|
|
6
6
|
const ProxyAgent = require("proxy-agent");
|
|
7
|
+
const retry = require("retry");
|
|
7
8
|
const abort_controller_1 = require("abort-controller");
|
|
8
9
|
const node_fetch_1 = require("node-fetch");
|
|
9
10
|
const util_1 = require("util");
|
|
@@ -151,6 +152,7 @@ class Client {
|
|
|
151
152
|
return `${this.opts.urlPrefix}${versionPath}${options.path}`;
|
|
152
153
|
}
|
|
153
154
|
async doRequest(options) {
|
|
155
|
+
var _a;
|
|
154
156
|
if (!options.path.startsWith("/")) {
|
|
155
157
|
options.path = "/" + options.path;
|
|
156
158
|
}
|
|
@@ -199,53 +201,94 @@ class Client {
|
|
|
199
201
|
else if (options.body !== undefined) {
|
|
200
202
|
fetchOptions.body = JSON.stringify(options.body);
|
|
201
203
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
catch (thrown) {
|
|
208
|
-
const err = thrown instanceof Error ? thrown : new Error(thrown);
|
|
209
|
-
const isAbortError = err.name.includes("AbortError");
|
|
210
|
-
if (isAbortError) {
|
|
211
|
-
throw new error_1.FirebaseError(`Timeout reached making request to ${fetchURL}`, { original: err });
|
|
212
|
-
}
|
|
213
|
-
throw new error_1.FirebaseError(`Failed to make request to ${fetchURL}`, { original: err });
|
|
214
|
-
}
|
|
215
|
-
finally {
|
|
216
|
-
if (reqTimeout) {
|
|
217
|
-
clearTimeout(reqTimeout);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
let body;
|
|
221
|
-
if (options.responseType === "json") {
|
|
222
|
-
const text = await res.text();
|
|
223
|
-
if (!text.length) {
|
|
224
|
-
body = undefined;
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
body = JSON.parse(text);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
else if (options.responseType === "stream") {
|
|
231
|
-
body = res.body;
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
throw new error_1.FirebaseError(`Unable to interpret response. Please set responseType.`, {
|
|
235
|
-
exit: 2,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
this.logResponse(res, body, options);
|
|
239
|
-
if (res.status >= 400) {
|
|
240
|
-
if (!options.resolveOnHTTPError) {
|
|
241
|
-
throw responseToError({ statusCode: res.status }, body);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
status: res.status,
|
|
246
|
-
response: res,
|
|
247
|
-
body,
|
|
204
|
+
const operationOptions = {
|
|
205
|
+
retries: ((_a = options.retryCodes) === null || _a === void 0 ? void 0 : _a.length) ? 1 : 2,
|
|
206
|
+
minTimeout: 1 * 1000,
|
|
207
|
+
maxTimeout: 5 * 1000,
|
|
248
208
|
};
|
|
209
|
+
if (typeof options.retries === "number") {
|
|
210
|
+
operationOptions.retries = options.retries;
|
|
211
|
+
}
|
|
212
|
+
if (typeof options.retryMinTimeout === "number") {
|
|
213
|
+
operationOptions.minTimeout = options.retryMinTimeout;
|
|
214
|
+
}
|
|
215
|
+
if (typeof options.retryMaxTimeout === "number") {
|
|
216
|
+
operationOptions.maxTimeout = options.retryMaxTimeout;
|
|
217
|
+
}
|
|
218
|
+
const operation = retry.operation(operationOptions);
|
|
219
|
+
return await new Promise((resolve, reject) => {
|
|
220
|
+
operation.attempt(async (currentAttempt) => {
|
|
221
|
+
var _a;
|
|
222
|
+
let res;
|
|
223
|
+
let body;
|
|
224
|
+
try {
|
|
225
|
+
if (currentAttempt > 1) {
|
|
226
|
+
logger_1.logger.debug(`*** [apiv2] Attempting the request again. Attempt number ${currentAttempt}`);
|
|
227
|
+
}
|
|
228
|
+
this.logRequest(options);
|
|
229
|
+
try {
|
|
230
|
+
res = await (0, node_fetch_1.default)(fetchURL, fetchOptions);
|
|
231
|
+
}
|
|
232
|
+
catch (thrown) {
|
|
233
|
+
const err = thrown instanceof Error ? thrown : new Error(thrown);
|
|
234
|
+
const isAbortError = err.name.includes("AbortError");
|
|
235
|
+
if (isAbortError) {
|
|
236
|
+
throw new error_1.FirebaseError(`Timeout reached making request to ${fetchURL}`, {
|
|
237
|
+
original: err,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
throw new error_1.FirebaseError(`Failed to make request to ${fetchURL}`, { original: err });
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
if (reqTimeout) {
|
|
244
|
+
clearTimeout(reqTimeout);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (options.responseType === "json") {
|
|
248
|
+
const text = await res.text();
|
|
249
|
+
if (!text.length) {
|
|
250
|
+
body = undefined;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
try {
|
|
254
|
+
body = JSON.parse(text);
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
throw new error_1.FirebaseError(`Unable to parse JSON: ${err}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
else if (options.responseType === "stream") {
|
|
262
|
+
body = res.body;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
throw new error_1.FirebaseError(`Unable to interpret response. Please set responseType.`, {
|
|
266
|
+
exit: 2,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
return err instanceof error_1.FirebaseError ? reject(err) : reject(new error_1.FirebaseError(`${err}`));
|
|
272
|
+
}
|
|
273
|
+
this.logResponse(res, body, options);
|
|
274
|
+
if (res.status >= 400) {
|
|
275
|
+
if ((_a = options.retryCodes) === null || _a === void 0 ? void 0 : _a.includes(res.status)) {
|
|
276
|
+
const err = responseToError({ statusCode: res.status }, body) || undefined;
|
|
277
|
+
if (operation.retry(err)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (!options.resolveOnHTTPError) {
|
|
282
|
+
return reject(responseToError({ statusCode: res.status }, body));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
resolve({
|
|
286
|
+
status: res.status,
|
|
287
|
+
response: res,
|
|
288
|
+
body,
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
});
|
|
249
292
|
}
|
|
250
293
|
logRequest(options) {
|
|
251
294
|
var _a, _b;
|
|
@@ -282,7 +325,7 @@ class Client {
|
|
|
282
325
|
}
|
|
283
326
|
exports.Client = Client;
|
|
284
327
|
function isLocalInsecureRequest(urlPrefix) {
|
|
285
|
-
const u =
|
|
328
|
+
const u = new url_1.URL(urlPrefix);
|
|
286
329
|
return u.protocol === "http:";
|
|
287
330
|
}
|
|
288
331
|
function bodyToString(body) {
|
package/lib/archiveDirectory.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.archiveDirectory = void 0;
|
|
3
4
|
const archiver = require("archiver");
|
|
4
5
|
const filesize = require("filesize");
|
|
5
6
|
const fs = require("fs");
|
|
6
7
|
const path = require("path");
|
|
7
8
|
const tar = require("tar");
|
|
8
9
|
const tmp = require("tmp");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
10
|
+
const error_1 = require("./error");
|
|
11
|
+
const listFiles_1 = require("./listFiles");
|
|
12
|
+
const logger_1 = require("./logger");
|
|
11
13
|
const fsAsync = require("./fsAsync");
|
|
12
|
-
|
|
13
|
-
const utils = require("./utils");
|
|
14
|
-
const archiveDirectory = (sourceDirectory, options) => {
|
|
15
|
-
options = options || {};
|
|
14
|
+
async function archiveDirectory(sourceDirectory, options = {}) {
|
|
16
15
|
let postfix = ".tar.gz";
|
|
17
16
|
if (options.type === "zip") {
|
|
18
17
|
postfix = ".zip";
|
|
@@ -26,105 +25,96 @@ const archiveDirectory = (sourceDirectory, options) => {
|
|
|
26
25
|
}
|
|
27
26
|
let makeArchive;
|
|
28
27
|
if (options.type === "zip") {
|
|
29
|
-
makeArchive =
|
|
28
|
+
makeArchive = zipDirectory(sourceDirectory, tempFile, options);
|
|
30
29
|
}
|
|
31
30
|
else {
|
|
32
|
-
makeArchive =
|
|
31
|
+
makeArchive = tarDirectory(sourceDirectory, tempFile, options);
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
logger.debug(`Archived ${filesize(archive.size)} in ${sourceDirectory}.`);
|
|
33
|
+
try {
|
|
34
|
+
const archive = await makeArchive;
|
|
35
|
+
logger_1.logger.debug(`Archived ${filesize(archive.size)} in ${sourceDirectory}.`);
|
|
37
36
|
return archive;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (err instanceof FirebaseError) {
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
if (err instanceof error_1.FirebaseError) {
|
|
41
40
|
throw err;
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const allFiles = listFiles(sourceDirectory, options.ignore);
|
|
42
|
+
throw new error_1.FirebaseError("Failed to create archive.", { original: err });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.archiveDirectory = archiveDirectory;
|
|
46
|
+
async function tarDirectory(sourceDirectory, tempFile, options) {
|
|
47
|
+
const allFiles = (0, listFiles_1.listFiles)(sourceDirectory, options.ignore);
|
|
50
48
|
try {
|
|
51
49
|
fs.statSync(sourceDirectory);
|
|
52
50
|
}
|
|
53
51
|
catch (err) {
|
|
54
52
|
if (err.code === "ENOENT") {
|
|
55
|
-
|
|
53
|
+
throw new error_1.FirebaseError(`Could not read directory "${sourceDirectory}"`);
|
|
56
54
|
}
|
|
57
55
|
throw err;
|
|
58
56
|
}
|
|
59
57
|
if (!allFiles.length) {
|
|
60
|
-
|
|
58
|
+
throw new error_1.FirebaseError(`Cannot create a tar archive with 0 files from directory "${sourceDirectory}"`);
|
|
61
59
|
}
|
|
62
|
-
|
|
63
|
-
.create({
|
|
60
|
+
await tar.create({
|
|
64
61
|
gzip: true,
|
|
65
62
|
file: tempFile.name,
|
|
66
63
|
cwd: sourceDirectory,
|
|
67
64
|
follow: true,
|
|
68
65
|
noDirRecurse: true,
|
|
69
66
|
portable: true,
|
|
70
|
-
}, allFiles)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
};
|
|
82
|
-
const _zipDirectory = (sourceDirectory, tempFile, options) => {
|
|
67
|
+
}, allFiles);
|
|
68
|
+
const stats = fs.statSync(tempFile.name);
|
|
69
|
+
return {
|
|
70
|
+
file: tempFile.name,
|
|
71
|
+
stream: fs.createReadStream(tempFile.name),
|
|
72
|
+
manifest: allFiles,
|
|
73
|
+
size: stats.size,
|
|
74
|
+
source: sourceDirectory,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function zipDirectory(sourceDirectory, tempFile, options) {
|
|
83
78
|
const archiveFileStream = fs.createWriteStream(tempFile.name, {
|
|
84
79
|
flags: "w",
|
|
85
80
|
encoding: "binary",
|
|
86
81
|
});
|
|
87
82
|
const archive = archiver("zip");
|
|
88
|
-
const archiveDone =
|
|
83
|
+
const archiveDone = pipeAsync(archive, archiveFileStream);
|
|
89
84
|
const allFiles = [];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
.
|
|
85
|
+
let files;
|
|
86
|
+
try {
|
|
87
|
+
files = await fsAsync.readdirRecursive({ path: sourceDirectory, ignore: options.ignore });
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
93
90
|
if (err.code === "ENOENT") {
|
|
94
|
-
|
|
91
|
+
throw new error_1.FirebaseError(`Could not read directory "${sourceDirectory}"`, { original: err });
|
|
95
92
|
}
|
|
96
93
|
throw err;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
mode: file.mode,
|
|
105
|
-
});
|
|
94
|
+
}
|
|
95
|
+
for (const file of files) {
|
|
96
|
+
const name = path.relative(sourceDirectory, file.name);
|
|
97
|
+
allFiles.push(name);
|
|
98
|
+
archive.file(file.name, {
|
|
99
|
+
name,
|
|
100
|
+
mode: file.mode,
|
|
106
101
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const _pipeAsync = function (from, to) {
|
|
122
|
-
return new Promise(function (resolve, reject) {
|
|
102
|
+
}
|
|
103
|
+
void archive.finalize();
|
|
104
|
+
await archiveDone;
|
|
105
|
+
const stats = fs.statSync(tempFile.name);
|
|
106
|
+
return {
|
|
107
|
+
file: tempFile.name,
|
|
108
|
+
stream: fs.createReadStream(tempFile.name),
|
|
109
|
+
manifest: allFiles,
|
|
110
|
+
size: stats.size,
|
|
111
|
+
source: sourceDirectory,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function pipeAsync(from, to) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
123
116
|
to.on("finish", resolve);
|
|
124
117
|
to.on("error", reject);
|
|
125
118
|
from.pipe(to);
|
|
126
119
|
});
|
|
127
|
-
}
|
|
128
|
-
module.exports = {
|
|
129
|
-
archiveDirectory,
|
|
130
|
-
};
|
|
120
|
+
}
|
|
@@ -58,14 +58,14 @@ module.exports = new command_1.Command("ext:dev:usage <publisherId>")
|
|
|
58
58
|
}
|
|
59
59
|
const profile = await (0, extensionsApi_1.getPublisherProfile)("-", publisherId);
|
|
60
60
|
const projectNumber = (0, extensionsHelper_1.getPublisherProjectFromName)(profile.name);
|
|
61
|
-
const
|
|
62
|
-
|
|
61
|
+
const past45d = new Date();
|
|
62
|
+
past45d.setDate(past45d.getDate() - 45);
|
|
63
63
|
const query = {
|
|
64
64
|
filter: `metric.type="firebaseextensions.googleapis.com/extension/version/active_instances" ` +
|
|
65
65
|
`resource.type="firebaseextensions.googleapis.com/ExtensionVersion" ` +
|
|
66
66
|
`resource.labels.extension="${extensionName}"`,
|
|
67
67
|
"interval.endTime": new Date().toJSON(),
|
|
68
|
-
"interval.startTime":
|
|
68
|
+
"interval.startTime": past45d.toJSON(),
|
|
69
69
|
view: cloudmonitoring_1.TimeSeriesView.FULL,
|
|
70
70
|
"aggregation.alignmentPeriod": (60 * 60 * 24).toString() + "s",
|
|
71
71
|
"aggregation.perSeriesAligner": cloudmonitoring_1.Aligner.ALIGN_MAX,
|
|
@@ -95,15 +95,10 @@ module.exports = new command_1.Command("ext:dev:usage <publisherId>")
|
|
|
95
95
|
});
|
|
96
96
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `showing usage stats for ${clc.bold(extensionName)}:`);
|
|
97
97
|
logger_1.logger.info(table.toString());
|
|
98
|
-
const link = await buildCloudMonitoringLink({
|
|
99
|
-
projectNumber: projectNumber,
|
|
100
|
-
extensionName,
|
|
101
|
-
});
|
|
102
98
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `How to read this table:`);
|
|
103
99
|
logger_1.logger.info(`* Due to privacy considerations, numbers are reported as ranges.`);
|
|
104
100
|
logger_1.logger.info(`* In the absence of significant changes, we will render a '-' symbol.`);
|
|
105
101
|
logger_1.logger.info(`* You will need more than 10 installs over a period of more than 28 days to render sufficient data.`);
|
|
106
|
-
logger_1.logger.info(`For more detail, visit: ${link}`);
|
|
107
102
|
});
|
|
108
103
|
async function buildCloudMonitoringLink(args) {
|
|
109
104
|
const pageState = {
|
package/lib/database/metadata.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.setRulesetLabels = exports.createRuleset = exports.getRulesetLabels = exports.getRuleset = exports.listAllRulesets = void 0;
|
|
4
|
-
const
|
|
4
|
+
const api_1 = require("../api");
|
|
5
|
+
const apiv2_1 = require("../apiv2");
|
|
5
6
|
const logger_1 = require("../logger");
|
|
6
7
|
const utils = require("../utils");
|
|
7
8
|
function handleErrorResponse(response) {
|
|
@@ -13,12 +14,9 @@ function handleErrorResponse(response) {
|
|
|
13
14
|
code: 2,
|
|
14
15
|
});
|
|
15
16
|
}
|
|
17
|
+
const apiClient = new apiv2_1.Client({ urlPrefix: api_1.rtdbMetadataOrigin });
|
|
16
18
|
async function listAllRulesets(databaseName) {
|
|
17
|
-
const response = await
|
|
18
|
-
auth: true,
|
|
19
|
-
origin: api.rtdbMetadataOrigin,
|
|
20
|
-
json: true,
|
|
21
|
-
});
|
|
19
|
+
const response = await apiClient.get(`/namespaces/${databaseName}/rulesets`, { resolveOnHTTPError: true });
|
|
22
20
|
if (response.status === 200) {
|
|
23
21
|
return response.body.rulesets;
|
|
24
22
|
}
|
|
@@ -26,11 +24,7 @@ async function listAllRulesets(databaseName) {
|
|
|
26
24
|
}
|
|
27
25
|
exports.listAllRulesets = listAllRulesets;
|
|
28
26
|
async function getRuleset(databaseName, rulesetId) {
|
|
29
|
-
const response = await
|
|
30
|
-
auth: true,
|
|
31
|
-
origin: api.rtdbMetadataOrigin,
|
|
32
|
-
json: true,
|
|
33
|
-
});
|
|
27
|
+
const response = await apiClient.get(`/namespaces/${databaseName}/rulesets/${rulesetId}`, { resolveOnHTTPError: true });
|
|
34
28
|
if (response.status === 200) {
|
|
35
29
|
return response.body;
|
|
36
30
|
}
|
|
@@ -38,9 +32,8 @@ async function getRuleset(databaseName, rulesetId) {
|
|
|
38
32
|
}
|
|
39
33
|
exports.getRuleset = getRuleset;
|
|
40
34
|
async function getRulesetLabels(databaseName) {
|
|
41
|
-
const response = await
|
|
42
|
-
|
|
43
|
-
origin: api.rtdbMetadataOrigin,
|
|
35
|
+
const response = await apiClient.get(`/namespaces/${databaseName}/ruleset_labels`, {
|
|
36
|
+
resolveOnHTTPError: true,
|
|
44
37
|
});
|
|
45
38
|
if (response.status === 200) {
|
|
46
39
|
return response.body;
|
|
@@ -49,23 +42,22 @@ async function getRulesetLabels(databaseName) {
|
|
|
49
42
|
}
|
|
50
43
|
exports.getRulesetLabels = getRulesetLabels;
|
|
51
44
|
async function createRuleset(databaseName, source) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
origin: utils.addSubdomain(api.realtimeOrigin, databaseName),
|
|
55
|
-
json: false,
|
|
56
|
-
data: source,
|
|
45
|
+
const localApiClient = new apiv2_1.Client({
|
|
46
|
+
urlPrefix: utils.addSubdomain(api_1.realtimeOrigin, databaseName),
|
|
57
47
|
});
|
|
48
|
+
const response = await localApiClient.post(`/.settings/rulesets.json`, source, { resolveOnHTTPError: true });
|
|
58
49
|
if (response.status === 200) {
|
|
59
|
-
return
|
|
50
|
+
return response.body.id;
|
|
60
51
|
}
|
|
61
52
|
return handleErrorResponse(response);
|
|
62
53
|
}
|
|
63
54
|
exports.createRuleset = createRuleset;
|
|
64
55
|
async function setRulesetLabels(databaseName, labels) {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
const localApiClient = new apiv2_1.Client({
|
|
57
|
+
urlPrefix: utils.addSubdomain(api_1.realtimeOrigin, databaseName),
|
|
58
|
+
});
|
|
59
|
+
const response = await localApiClient.put(`/.settings/ruleset_labels.json`, labels, {
|
|
60
|
+
resolveOnHTTPError: true,
|
|
69
61
|
});
|
|
70
62
|
if (response.status === 200) {
|
|
71
63
|
return response.body;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.of = exports.empty = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isHttpsTriggered = exports.SCHEDULED_FUNCTION_LABEL = exports.memoryOptionDisplayName = exports.endpointTriggerType = void 0;
|
|
3
|
+
exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.of = exports.empty = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isHttpsTriggered = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_MEMORY_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.memoryOptionDisplayName = exports.endpointTriggerType = void 0;
|
|
4
4
|
const gcf = require("../../gcp/cloudfunctions");
|
|
5
5
|
const gcfV2 = require("../../gcp/cloudfunctionsv2");
|
|
6
6
|
const utils = require("../../utils");
|
|
@@ -36,6 +36,8 @@ function memoryOptionDisplayName(option) {
|
|
|
36
36
|
}[option];
|
|
37
37
|
}
|
|
38
38
|
exports.memoryOptionDisplayName = memoryOptionDisplayName;
|
|
39
|
+
exports.DEFAULT_MEMORY = 256;
|
|
40
|
+
exports.MIN_MEMORY_FOR_CONCURRENCY = 2048;
|
|
39
41
|
exports.SCHEDULED_FUNCTION_LABEL = Object.freeze({ deployment: "firebase-schedule" });
|
|
40
42
|
function isHttpsTriggered(triggered) {
|
|
41
43
|
return {}.hasOwnProperty.call(triggered, "httpsTrigger");
|
|
@@ -27,7 +27,7 @@ function hasDotenv(opts) {
|
|
|
27
27
|
return previews_1.previews.dotenv && functionsEnv.hasUserEnvs(opts);
|
|
28
28
|
}
|
|
29
29
|
async function maybeEnableAR(projectId) {
|
|
30
|
-
if (previews_1.previews.artifactregistry) {
|
|
30
|
+
if (!previews_1.previews.artifactregistry) {
|
|
31
31
|
return ensureApiEnabled.check(projectId, "artifactregistry.googleapis.com", "functions", true);
|
|
32
32
|
}
|
|
33
33
|
await ensureApiEnabled.ensure(projectId, "artifactregistry.googleapis.com", "functions");
|
|
@@ -113,7 +113,7 @@ async function prepare(context, options, payload) {
|
|
|
113
113
|
await Promise.all(Object.values(wantBackend.requiredAPIs).map((api) => {
|
|
114
114
|
return ensureApiEnabled.ensure(projectId, api, "functions", false);
|
|
115
115
|
}));
|
|
116
|
-
validate.
|
|
116
|
+
validate.endpointsAreValid(wantBackend);
|
|
117
117
|
context.filters = (0, functionsDeployHelper_1.getFilterGroups)(options);
|
|
118
118
|
const matchingBackend = backend.matchingBackend(wantBackend, (endpoint) => {
|
|
119
119
|
return (0, functionsDeployHelper_1.functionMatchesAnyGroup)(endpoint, context.filters);
|
|
@@ -242,7 +242,10 @@ class Fabricator {
|
|
|
242
242
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
|
-
|
|
245
|
+
const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
|
|
246
|
+
if (mem >= backend.MIN_MEMORY_FOR_CONCURRENCY && endpoint.concurrency != 1) {
|
|
247
|
+
await this.setConcurrency(endpoint, serviceName, endpoint.concurrency || DEFAULT_GCFV2_CONCURRENCY);
|
|
248
|
+
}
|
|
246
249
|
}
|
|
247
250
|
async updateV1Function(endpoint, scraper) {
|
|
248
251
|
var _a;
|
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.functionIdsAreValid = exports.functionsDirectoryExists = void 0;
|
|
3
|
+
exports.functionIdsAreValid = exports.functionsDirectoryExists = exports.endpointsAreValid = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const error_1 = require("../../error");
|
|
7
7
|
const fsutils = require("../../fsutils");
|
|
8
|
+
const backend = require("./backend");
|
|
9
|
+
function endpointsAreValid(wantBackend) {
|
|
10
|
+
functionIdsAreValid(backend.allEndpoints(wantBackend));
|
|
11
|
+
const gcfV1WithConcurrency = backend
|
|
12
|
+
.allEndpoints(wantBackend)
|
|
13
|
+
.filter((endpoint) => (endpoint.concurrency || 1) != 1 && endpoint.platform == "gcfv1")
|
|
14
|
+
.map((endpoint) => endpoint.id);
|
|
15
|
+
if (gcfV1WithConcurrency.length) {
|
|
16
|
+
const msg = `Cannot set concurrency on the functions ${gcfV1WithConcurrency.join(",")} because they are GCF gen 1`;
|
|
17
|
+
throw new error_1.FirebaseError(msg);
|
|
18
|
+
}
|
|
19
|
+
const tooSmallForConcurrency = backend
|
|
20
|
+
.allEndpoints(wantBackend)
|
|
21
|
+
.filter((endpoint) => {
|
|
22
|
+
if ((endpoint.concurrency || 1) == 1) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
|
|
26
|
+
return mem < backend.MIN_MEMORY_FOR_CONCURRENCY;
|
|
27
|
+
})
|
|
28
|
+
.map((endpoint) => endpoint.id);
|
|
29
|
+
if (tooSmallForConcurrency.length) {
|
|
30
|
+
const msg = `Cannot set concurency on the functions ${tooSmallForConcurrency.join(",")} because they have fewer than 2GB memory`;
|
|
31
|
+
throw new error_1.FirebaseError(msg);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.endpointsAreValid = endpointsAreValid;
|
|
8
35
|
function functionsDirectoryExists(sourceDir, projectDir) {
|
|
9
36
|
if (!fsutils.dirExistsSync(sourceDir)) {
|
|
10
37
|
const sourceDirName = path.relative(projectDir, sourceDir);
|