firebase-tools 13.22.1 → 13.23.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/apiv2.js CHANGED
@@ -303,13 +303,13 @@ class Client {
303
303
  setAccessToken(await getAccessToken());
304
304
  }
305
305
  if ((_a = options.retryCodes) === null || _a === void 0 ? void 0 : _a.includes(res.status)) {
306
- const err = (0, responseToError_1.responseToError)({ statusCode: res.status }, body) || undefined;
306
+ const err = (0, responseToError_1.responseToError)({ statusCode: res.status }, body, fetchURL) || undefined;
307
307
  if (operation.retry(err)) {
308
308
  return;
309
309
  }
310
310
  }
311
311
  if (!options.resolveOnHTTPError) {
312
- return reject((0, responseToError_1.responseToError)({ statusCode: res.status }, body));
312
+ return reject((0, responseToError_1.responseToError)({ statusCode: res.status }, body, fetchURL));
313
313
  }
314
314
  }
315
315
  resolve({
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getGitHubCommit = exports.getGitHubBranch = exports.fetchRepositoryCloneUris = exports.listAppHostingConnections = exports.getOrCreateRepository = exports.getOrCreateConnection = exports.createConnection = exports.ensureSecretManagerAdminGrant = exports.promptGitHubBranch = exports.getOrCreateOauthConnection = exports.listValidInstallations = exports.promptGitHubInstallation = exports.getConnectionForInstallation = exports.linkGitHubRepository = exports.generateRepositoryId = exports.extractRepoSlugFromUri = exports.parseConnectionName = void 0;
3
+ exports.getGitHubCommit = exports.getGitHubBranch = exports.fetchRepositoryCloneUris = exports.listAppHostingConnections = exports.getOrCreateRepository = exports.getOrCreateConnection = exports.createConnection = exports.ensureSecretManagerAdminGrant = exports.promptGitHubBranch = exports.getOrCreateOauthConnection = exports.listValidInstallations = exports.promptGitHubInstallation = exports.getConnectionForInstallation = exports.linkGitHubRepository = exports.getOrCreateGithubConnectionWithSentinel = exports.generateRepositoryId = exports.extractRepoSlugFromUri = exports.parseConnectionName = void 0;
4
4
  const clc = require("colorette");
5
5
  const devConnect = require("../gcp/devConnect");
6
6
  const rm = require("../gcp/resourceManager");
@@ -55,9 +55,20 @@ function generateConnectionId() {
55
55
  }
56
56
  const ADD_ACCOUNT_CHOICE = "@ADD_ACCOUNT";
57
57
  const MANAGE_INSTALLATION_CHOICE = "@MANAGE_INSTALLATION";
58
- async function linkGitHubRepository(projectId, location) {
59
- var _a, _b;
58
+ async function getOrCreateGithubConnectionWithSentinel(projectId, location, createConnectionId) {
60
59
  utils.logBullet(clc.bold(`${clc.yellow("===")} Import a GitHub repository`));
60
+ if (createConnectionId) {
61
+ try {
62
+ const connection = await devConnect.getConnection(projectId, location, createConnectionId);
63
+ utils.logBullet(`Reusing existing connection ${createConnectionId}`);
64
+ return connection;
65
+ }
66
+ catch (err) {
67
+ if (err.status !== 404) {
68
+ throw err;
69
+ }
70
+ }
71
+ }
61
72
  const oauthConn = await getOrCreateOauthConnection(projectId, location);
62
73
  let installationId = await promptGitHubInstallation(projectId, location, oauthConn);
63
74
  while (installationId === ADD_ACCOUNT_CHOICE) {
@@ -71,21 +82,35 @@ async function linkGitHubRepository(projectId, location) {
71
82
  });
72
83
  installationId = await promptGitHubInstallation(projectId, location, oauthConn);
73
84
  }
74
- let connectionMatchingInstallation = await getConnectionForInstallation(projectId, location, installationId);
75
- if (!connectionMatchingInstallation) {
76
- connectionMatchingInstallation = await createFullyInstalledConnection(projectId, location, generateConnectionId(), oauthConn, installationId);
85
+ const connectionMatchingInstallation = await getConnectionForInstallation(projectId, location, installationId);
86
+ if (connectionMatchingInstallation) {
87
+ const { id: matchingConnectionId } = parseConnectionName(connectionMatchingInstallation.name);
88
+ if (!createConnectionId) {
89
+ utils.logBullet(`Reusing matching connection ${matchingConnectionId}`);
90
+ return connectionMatchingInstallation;
91
+ }
92
+ }
93
+ if (!createConnectionId) {
94
+ createConnectionId = generateConnectionId();
77
95
  }
96
+ const connection = await createFullyInstalledConnection(projectId, location, createConnectionId, oauthConn, installationId);
97
+ return connection;
98
+ }
99
+ exports.getOrCreateGithubConnectionWithSentinel = getOrCreateGithubConnectionWithSentinel;
100
+ async function linkGitHubRepository(projectId, location, createConnectionId) {
101
+ var _a, _b;
102
+ const connection = await getOrCreateGithubConnectionWithSentinel(projectId, location, createConnectionId);
78
103
  let repoCloneUri;
79
104
  do {
80
105
  if (repoCloneUri === MANAGE_INSTALLATION_CHOICE) {
81
- await manageInstallation(connectionMatchingInstallation);
106
+ await manageInstallation(connection);
82
107
  }
83
- repoCloneUri = await promptCloneUri(projectId, connectionMatchingInstallation);
108
+ repoCloneUri = await promptCloneUri(projectId, connection);
84
109
  } while (repoCloneUri === MANAGE_INSTALLATION_CHOICE);
85
- const { id: connectionId } = parseConnectionName(connectionMatchingInstallation.name);
110
+ const { id: connectionId } = parseConnectionName(connection.name);
86
111
  await getOrCreateConnection(projectId, location, connectionId, {
87
- authorizerCredential: (_a = connectionMatchingInstallation.githubConfig) === null || _a === void 0 ? void 0 : _a.authorizerCredential,
88
- appInstallationId: (_b = connectionMatchingInstallation.githubConfig) === null || _b === void 0 ? void 0 : _b.appInstallationId,
112
+ authorizerCredential: (_a = connection.githubConfig) === null || _a === void 0 ? void 0 : _a.authorizerCredential,
113
+ appInstallationId: (_b = connection.githubConfig) === null || _b === void 0 ? void 0 : _b.appInstallationId,
89
114
  });
90
115
  const repo = await getOrCreateRepository(projectId, location, connectionId, repoCloneUri);
91
116
  return repo;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getBackendForAmbiguousLocation = exports.promptLocation = exports.deleteBackendAndPoll = exports.setDefaultTrafficPolicy = exports.createBackend = exports.ensureAppHostingComputeServiceAccount = exports.doSetup = void 0;
3
+ exports.getBackendForAmbiguousLocation = exports.promptLocation = exports.deleteBackendAndPoll = exports.setDefaultTrafficPolicy = exports.createBackend = exports.ensureAppHostingComputeServiceAccount = exports.createGitRepoLink = exports.doSetup = void 0;
4
4
  const clc = require("colorette");
5
5
  const poller = require("../operation-poller");
6
6
  const apphosting = require("../gcp/apphosting");
@@ -128,6 +128,24 @@ async function doSetup(projectId, webAppName, location, serviceAccount) {
128
128
  (0, utils_1.logSuccess)(`Your backend is now deployed at:\n\thttps://${backend.uri}`);
129
129
  }
130
130
  exports.doSetup = doSetup;
131
+ async function createGitRepoLink(projectId, location, connectionId) {
132
+ await Promise.all([
133
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.developerConnectOrigin)(), "apphosting", true),
134
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.secretManagerOrigin)(), "apphosting", true),
135
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.iamOrigin)(), "apphosting", true),
136
+ ]);
137
+ const allowedLocations = (await apphosting.listLocations(projectId)).map((loc) => loc.locationId);
138
+ if (location) {
139
+ if (!allowedLocations.includes(location)) {
140
+ throw new error_1.FirebaseError(`Invalid location ${location}. Valid choices are ${allowedLocations.join(", ")}`);
141
+ }
142
+ }
143
+ location =
144
+ location ||
145
+ (await promptLocation(projectId, "Select a location for your GitRepoLink's connection:\n"));
146
+ await githubConnections.linkGitHubRepository(projectId, location, connectionId);
147
+ }
148
+ exports.createGitRepoLink = createGitRepoLink;
131
149
  async function ensureAppHostingComputeServiceAccount(projectId, serviceAccount) {
132
150
  const sa = serviceAccount || defaultComputeServiceAccountEmail(projectId);
133
151
  const name = `projects/${projectId}/serviceAccounts/${sa}`;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const projectUtils_1 = require("../projectUtils");
6
+ const requireInteractive_1 = require("../requireInteractive");
7
+ const apphosting_1 = require("../apphosting");
8
+ const apphosting_2 = require("../gcp/apphosting");
9
+ const firedata_1 = require("../gcp/firedata");
10
+ const requireTosAcceptance_1 = require("../requireTosAcceptance");
11
+ exports.command = new command_1.Command("apphosting:repos:create")
12
+ .description("create a Firebase App Hosting Developer Connect Git Repository Link")
13
+ .option("-l, --location <location>", "specify the location of the backend", "")
14
+ .option("-g, --gitconnection <connection>", "id of the connection", "")
15
+ .before(apphosting_2.ensureApiEnabled)
16
+ .before(requireInteractive_1.default)
17
+ .before((0, requireTosAcceptance_1.requireTosAcceptance)(firedata_1.APPHOSTING_TOS_ID))
18
+ .action(async (options) => {
19
+ const projectId = (0, projectUtils_1.needProjectId)(options);
20
+ const location = options.location;
21
+ const connection = options.gitconnection;
22
+ await (0, apphosting_1.createGitRepoLink)(projectId, location, connection);
23
+ });
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const pg = require("pg");
5
+ const clc = require("colorette");
6
+ const cloud_sql_connector_1 = require("@google-cloud/cloud-sql-connector");
7
+ const command_1 = require("../command");
8
+ const projectUtils_1 = require("../projectUtils");
9
+ const ensureApis_1 = require("../dataconnect/ensureApis");
10
+ const requirePermissions_1 = require("../requirePermissions");
11
+ const fileUtils_1 = require("../dataconnect/fileUtils");
12
+ const schemaMigration_1 = require("../dataconnect/schemaMigration");
13
+ const requireAuth_1 = require("../requireAuth");
14
+ const connect_1 = require("../gcp/cloudsql/connect");
15
+ const cloudSqlAdminClient = require("../gcp/cloudsql/cloudsqladmin");
16
+ const prompt_1 = require("../prompt");
17
+ const logger_1 = require("../logger");
18
+ const error_1 = require("../error");
19
+ const fbToolsAuthClient_1 = require("../gcp/cloudsql/fbToolsAuthClient");
20
+ const interactive_1 = require("../gcp/cloudsql/interactive");
21
+ const sqlKeywords = [
22
+ "SELECT",
23
+ "FROM",
24
+ "WHERE",
25
+ "INSERT",
26
+ "UPDATE",
27
+ "DELETE",
28
+ "JOIN",
29
+ "GROUP",
30
+ "ORDER",
31
+ "LIMIT",
32
+ "GRANT",
33
+ "CREATE",
34
+ "DROP",
35
+ ];
36
+ async function promptForQuery() {
37
+ let query = "";
38
+ let line = "";
39
+ do {
40
+ const question = {
41
+ type: "input",
42
+ name: "line",
43
+ message: query ? "> " : "Enter your SQL query (or '.exit'):",
44
+ transformer: (input) => {
45
+ return input
46
+ .split(" ")
47
+ .map((word) => (sqlKeywords.includes(word.toUpperCase()) ? clc.cyan(word) : word))
48
+ .join(" ");
49
+ },
50
+ };
51
+ ({ line } = await (0, prompt_1.prompt)({ nonInteractive: false }, [question]));
52
+ line = line.trimEnd();
53
+ if (line.toLowerCase() === ".exit") {
54
+ return ".exit";
55
+ }
56
+ query += (query ? "\n" : "") + line;
57
+ } while (line !== "" && !query.endsWith(";"));
58
+ return query;
59
+ }
60
+ async function mainShellLoop(conn) {
61
+ while (true) {
62
+ const query = await promptForQuery();
63
+ if (query.toLowerCase() === ".exit") {
64
+ break;
65
+ }
66
+ if (query === "") {
67
+ continue;
68
+ }
69
+ if (await (0, interactive_1.confirmDangerousQuery)(query)) {
70
+ await (0, interactive_1.interactiveExecuteQuery)(query, conn);
71
+ }
72
+ else {
73
+ logger_1.logger.info(clc.yellow("Query cancelled."));
74
+ }
75
+ }
76
+ }
77
+ exports.command = new command_1.Command("dataconnect:sql:shell [serviceId]")
78
+ .description("Starts a shell connected directly to your dataconnect cloudsql instance.")
79
+ .before(requirePermissions_1.requirePermissions, ["firebasedataconnect.services.list", "cloudsql.instances.connect"])
80
+ .before(requireAuth_1.requireAuth)
81
+ .action(async (serviceId, options) => {
82
+ const projectId = (0, projectUtils_1.needProjectId)(options);
83
+ await (0, ensureApis_1.ensureApis)(projectId);
84
+ const serviceInfo = await (0, fileUtils_1.pickService)(projectId, options.config, serviceId);
85
+ const { instanceId, databaseId } = (0, schemaMigration_1.getIdentifiers)(serviceInfo.schema);
86
+ const { user: username } = await (0, connect_1.getIAMUser)(options);
87
+ const instance = await cloudSqlAdminClient.getInstance(projectId, instanceId);
88
+ const connectionName = instance.connectionName;
89
+ if (!connectionName) {
90
+ throw new error_1.FirebaseError(`Could not get instance connection string for ${options.instanceId}:${options.databaseId}`);
91
+ }
92
+ const connector = new cloud_sql_connector_1.Connector({
93
+ auth: new fbToolsAuthClient_1.FBToolsAuthClient(),
94
+ });
95
+ const clientOpts = await connector.getOptions({
96
+ instanceConnectionName: connectionName,
97
+ ipType: cloud_sql_connector_1.IpAddressTypes.PUBLIC,
98
+ authType: cloud_sql_connector_1.AuthTypes.IAM,
99
+ });
100
+ const pool = new pg.Pool(Object.assign(Object.assign({}, clientOpts), { user: username, database: databaseId }));
101
+ const conn = await pool.connect();
102
+ logger_1.logger.info(`Logged in as ${username}`);
103
+ logger_1.logger.info(clc.cyan("Welcome to Data Connect Cloud SQL Shell"));
104
+ logger_1.logger.info(clc.gray("Type your your SQL query or '.exit' to quit, queries should end with ';' or add empty line to execute."));
105
+ await mainShellLoop(conn);
106
+ logger_1.logger.info(clc.yellow("Exiting shell..."));
107
+ conn.release();
108
+ await pool.end();
109
+ connector.close();
110
+ return { projectId, serviceId };
111
+ });
@@ -173,6 +173,8 @@ function load(client) {
173
173
  client.apphosting.builds = {};
174
174
  client.apphosting.builds.get = loadCommand("apphosting-builds-get");
175
175
  client.apphosting.builds.create = loadCommand("apphosting-builds-create");
176
+ client.apphosting.repos = {};
177
+ client.apphosting.repos.create = loadCommand("apphosting-repos-create");
176
178
  client.apphosting.rollouts = {};
177
179
  client.apphosting.rollouts.create = loadCommand("apphosting-rollouts-create");
178
180
  client.apphosting.rollouts.list = loadCommand("apphosting-rollouts-list");
@@ -210,6 +212,7 @@ function load(client) {
210
212
  client.dataconnect.sql.diff = loadCommand("dataconnect-sql-diff");
211
213
  client.dataconnect.sql.migrate = loadCommand("dataconnect-sql-migrate");
212
214
  client.dataconnect.sql.grant = loadCommand("dataconnect-sql-grant");
215
+ client.dataconnect.sql.shell = loadCommand("dataconnect-sql-shell");
213
216
  client.dataconnect.sdk = {};
214
217
  client.dataconnect.sdk.generate = loadCommand("dataconnect-sdk-generate");
215
218
  client.target = loadCommand("target");
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.grantRoleToUserInSchema = exports.migrateSchema = exports.diffSchema = void 0;
3
+ exports.getIdentifiers = exports.grantRoleToUserInSchema = exports.migrateSchema = exports.diffSchema = void 0;
4
4
  const clc = require("colorette");
5
5
  const sql_formatter_1 = require("sql-formatter");
6
6
  const types_1 = require("./types");
@@ -212,6 +212,7 @@ function getIdentifiers(schema) {
212
212
  serviceName,
213
213
  };
214
214
  }
215
+ exports.getIdentifiers = getIdentifiers;
215
216
  function suggestedCommand(serviceName, invalidConnectorNames) {
216
217
  const serviceId = serviceName.split("/")[5];
217
218
  const connectorIds = invalidConnectorNames.map((i) => i.split("/")[7]);
@@ -27,18 +27,22 @@ class AuthBlockingService {
27
27
  }
28
28
  }
29
29
  configChanged(newConfig, config) {
30
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
30
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
31
31
  if (((_b = (_a = newConfig.triggers) === null || _a === void 0 ? void 0 : _a.beforeCreate) === null || _b === void 0 ? void 0 : _b.functionUri) !==
32
32
  ((_d = (_c = config.triggers) === null || _c === void 0 ? void 0 : _c.beforeCreate) === null || _d === void 0 ? void 0 : _d.functionUri) ||
33
- ((_f = (_e = newConfig.triggers) === null || _e === void 0 ? void 0 : _e.beforeSignIn) === null || _f === void 0 ? void 0 : _f.functionUri) !== ((_h = (_g = config.triggers) === null || _g === void 0 ? void 0 : _g.beforeSignIn) === null || _h === void 0 ? void 0 : _h.functionUri)) {
33
+ ((_f = (_e = newConfig.triggers) === null || _e === void 0 ? void 0 : _e.beforeSignIn) === null || _f === void 0 ? void 0 : _f.functionUri) !==
34
+ ((_h = (_g = config.triggers) === null || _g === void 0 ? void 0 : _g.beforeSignIn) === null || _h === void 0 ? void 0 : _h.functionUri) ||
35
+ ((_k = (_j = newConfig.triggers) === null || _j === void 0 ? void 0 : _j.beforeSendEmail) === null || _k === void 0 ? void 0 : _k.functionUri) !==
36
+ ((_m = (_l = config.triggers) === null || _l === void 0 ? void 0 : _l.beforeSendEmail) === null || _m === void 0 ? void 0 : _m.functionUri) ||
37
+ ((_p = (_o = newConfig.triggers) === null || _o === void 0 ? void 0 : _o.beforeSendSms) === null || _p === void 0 ? void 0 : _p.functionUri) !== ((_r = (_q = config.triggers) === null || _q === void 0 ? void 0 : _q.beforeSendSms) === null || _r === void 0 ? void 0 : _r.functionUri)) {
34
38
  return true;
35
39
  }
36
- if (!!((_j = newConfig.forwardInboundCredentials) === null || _j === void 0 ? void 0 : _j.accessToken) !==
37
- !!((_k = config.forwardInboundCredentials) === null || _k === void 0 ? void 0 : _k.accessToken) ||
38
- !!((_l = newConfig.forwardInboundCredentials) === null || _l === void 0 ? void 0 : _l.idToken) !==
39
- !!((_m = config.forwardInboundCredentials) === null || _m === void 0 ? void 0 : _m.idToken) ||
40
- !!((_o = newConfig.forwardInboundCredentials) === null || _o === void 0 ? void 0 : _o.refreshToken) !==
41
- !!((_p = config.forwardInboundCredentials) === null || _p === void 0 ? void 0 : _p.refreshToken)) {
40
+ if (!!((_s = newConfig.forwardInboundCredentials) === null || _s === void 0 ? void 0 : _s.accessToken) !==
41
+ !!((_t = config.forwardInboundCredentials) === null || _t === void 0 ? void 0 : _t.accessToken) ||
42
+ !!((_u = newConfig.forwardInboundCredentials) === null || _u === void 0 ? void 0 : _u.idToken) !==
43
+ !!((_v = config.forwardInboundCredentials) === null || _v === void 0 ? void 0 : _v.idToken) ||
44
+ !!((_w = newConfig.forwardInboundCredentials) === null || _w === void 0 ? void 0 : _w.refreshToken) !==
45
+ !!((_x = config.forwardInboundCredentials) === null || _x === void 0 ? void 0 : _x.refreshToken)) {
42
46
  return true;
43
47
  }
44
48
  return false;
@@ -51,11 +55,24 @@ class AuthBlockingService {
51
55
  functionUri: endpoint.uri,
52
56
  } });
53
57
  }
54
- else {
58
+ else if (endpoint.blockingTrigger.eventType === events.v1.BEFORE_SIGN_IN_EVENT) {
55
59
  newBlockingConfig.triggers = Object.assign(Object.assign({}, newBlockingConfig.triggers), { beforeSignIn: {
56
60
  functionUri: endpoint.uri,
57
61
  } });
58
62
  }
63
+ else if (endpoint.blockingTrigger.eventType === events.v1.BEFORE_SEND_EMAIL_EVENT) {
64
+ newBlockingConfig.triggers = Object.assign(Object.assign({}, newBlockingConfig.triggers), { beforeSendEmail: {
65
+ functionUri: endpoint.uri,
66
+ } });
67
+ }
68
+ else if (endpoint.blockingTrigger.eventType === events.v1.BEFORE_SEND_SMS_EVENT) {
69
+ newBlockingConfig.triggers = Object.assign(Object.assign({}, newBlockingConfig.triggers), { beforeSendSms: {
70
+ functionUri: endpoint.uri,
71
+ } });
72
+ }
73
+ else {
74
+ throw new error_1.FirebaseError(`Received invalid blocking trigger event type ${endpoint.blockingTrigger.eventType}`);
75
+ }
59
76
  newBlockingConfig.forwardInboundCredentials = Object.assign(Object.assign({}, oldBlockingConfig.forwardInboundCredentials), endpoint.blockingTrigger.options);
60
77
  if (!this.configChanged(newBlockingConfig, oldBlockingConfig)) {
61
78
  return;
@@ -70,17 +87,25 @@ class AuthBlockingService {
70
87
  return this.triggerQueue;
71
88
  }
72
89
  async unregisterTriggerLocked(endpoint) {
73
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
90
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
74
91
  const blockingConfig = await identityPlatform.getBlockingFunctionsConfig(endpoint.project);
75
92
  if (endpoint.uri !== ((_b = (_a = blockingConfig.triggers) === null || _a === void 0 ? void 0 : _a.beforeCreate) === null || _b === void 0 ? void 0 : _b.functionUri) &&
76
- endpoint.uri !== ((_d = (_c = blockingConfig.triggers) === null || _c === void 0 ? void 0 : _c.beforeSignIn) === null || _d === void 0 ? void 0 : _d.functionUri)) {
93
+ endpoint.uri !== ((_d = (_c = blockingConfig.triggers) === null || _c === void 0 ? void 0 : _c.beforeSignIn) === null || _d === void 0 ? void 0 : _d.functionUri) &&
94
+ endpoint.uri !== ((_f = (_e = blockingConfig.triggers) === null || _e === void 0 ? void 0 : _e.beforeSendEmail) === null || _f === void 0 ? void 0 : _f.functionUri) &&
95
+ endpoint.uri !== ((_h = (_g = blockingConfig.triggers) === null || _g === void 0 ? void 0 : _g.beforeSendSms) === null || _h === void 0 ? void 0 : _h.functionUri)) {
77
96
  return;
78
97
  }
79
- if (endpoint.uri === ((_f = (_e = blockingConfig.triggers) === null || _e === void 0 ? void 0 : _e.beforeCreate) === null || _f === void 0 ? void 0 : _f.functionUri)) {
80
- (_g = blockingConfig.triggers) === null || _g === void 0 ? true : delete _g.beforeCreate;
98
+ if (endpoint.uri === ((_k = (_j = blockingConfig.triggers) === null || _j === void 0 ? void 0 : _j.beforeCreate) === null || _k === void 0 ? void 0 : _k.functionUri)) {
99
+ (_l = blockingConfig.triggers) === null || _l === void 0 ? true : delete _l.beforeCreate;
100
+ }
101
+ if (endpoint.uri === ((_o = (_m = blockingConfig.triggers) === null || _m === void 0 ? void 0 : _m.beforeSignIn) === null || _o === void 0 ? void 0 : _o.functionUri)) {
102
+ (_p = blockingConfig.triggers) === null || _p === void 0 ? true : delete _p.beforeSignIn;
103
+ }
104
+ if (endpoint.uri === ((_r = (_q = blockingConfig.triggers) === null || _q === void 0 ? void 0 : _q.beforeSendEmail) === null || _r === void 0 ? void 0 : _r.functionUri)) {
105
+ (_s = blockingConfig.triggers) === null || _s === void 0 ? true : delete _s.beforeSendEmail;
81
106
  }
82
- if (endpoint.uri === ((_j = (_h = blockingConfig.triggers) === null || _h === void 0 ? void 0 : _h.beforeSignIn) === null || _j === void 0 ? void 0 : _j.functionUri)) {
83
- (_k = blockingConfig.triggers) === null || _k === void 0 ? true : delete _k.beforeSignIn;
107
+ if (endpoint.uri === ((_u = (_t = blockingConfig.triggers) === null || _t === void 0 ? void 0 : _t.beforeSendSms) === null || _u === void 0 ? void 0 : _u.functionUri)) {
108
+ (_v = blockingConfig.triggers) === null || _v === void 0 ? true : delete _v.beforeSendSms;
84
109
  }
85
110
  await identityPlatform.setBlockingFunctionsConfig(endpoint.project, blockingConfig);
86
111
  }
@@ -94,6 +94,8 @@ const EVENT_SERVICE_MAPPING = {
94
94
  "google.firebase.firebasealerts.alerts.v1.published": firebaseAlertsService,
95
95
  "providers/cloud.auth/eventTypes/user.beforeCreate": authBlockingService,
96
96
  "providers/cloud.auth/eventTypes/user.beforeSignIn": authBlockingService,
97
+ "providers/cloud.auth/eventTypes/user.beforeSendEmail": authBlockingService,
98
+ "providers/cloud.auth/eventTypes/user.beforeSendSms": authBlockingService,
97
99
  "google.firebase.database.ref.v1.written": databaseService,
98
100
  "google.firebase.database.ref.v1.created": databaseService,
99
101
  "google.firebase.database.ref.v1.updated": databaseService,
@@ -256,13 +256,11 @@ class FunctionsEmulator {
256
256
  }
257
257
  async start() {
258
258
  const credentialEnv = await this.getCredentialsEnvironment();
259
- console.log("got creds env");
260
259
  for (const e of this.staticBackends) {
261
260
  e.env = Object.assign(Object.assign({}, credentialEnv), e.env);
262
261
  }
263
262
  if (Object.keys(this.adminSdkConfig || {}).length <= 1) {
264
263
  const adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this.args.projectId);
265
- console.log("got admin sdk");
266
264
  if (adminSdkConfig) {
267
265
  this.adminSdkConfig = adminSdkConfig;
268
266
  }
@@ -237,6 +237,17 @@ class FirestoreDelete {
237
237
  }
238
238
  queue.unshift(...toDelete);
239
239
  }
240
+ else if (e.status === 429 &&
241
+ this.deleteBatchSize >= 10 &&
242
+ e.message.includes("database has exceeded their maximum bandwidth")) {
243
+ logger_1.logger.debug("Database has exceeded maximum write bandwidth", e);
244
+ const newBatchSize = Math.floor(toDelete.length / 2);
245
+ if (newBatchSize < this.deleteBatchSize) {
246
+ utils.logLabeledWarning("firestore", `delete rate exceeding maximum bandwidth, reducing batch size from ${this.deleteBatchSize} to ${newBatchSize}`);
247
+ this.setDeleteBatchSize(newBatchSize);
248
+ }
249
+ queue.unshift(...toDelete);
250
+ }
240
251
  else if (e.status >= 500 && e.status < 600) {
241
252
  logger_1.logger.debug("Server error deleting doc batch", e);
242
253
  toDelete.forEach((doc) => {
@@ -7,8 +7,12 @@ exports.BLOCKING_LABEL = "deployment-blocking";
7
7
  exports.BLOCKING_LABEL_KEY_TO_EVENT = {
8
8
  "before-create": "providers/cloud.auth/eventTypes/user.beforeCreate",
9
9
  "before-sign-in": "providers/cloud.auth/eventTypes/user.beforeSignIn",
10
+ "before-send-email": "providers/cloud.auth/eventTypes/user.beforeSendEmail",
11
+ "before-send-sms": "providers/cloud.auth/eventTypes/user.beforeSendSms",
10
12
  };
11
13
  exports.BLOCKING_EVENT_TO_LABEL_KEY = {
12
14
  "providers/cloud.auth/eventTypes/user.beforeCreate": "before-create",
13
15
  "providers/cloud.auth/eventTypes/user.beforeSignIn": "before-sign-in",
16
+ "providers/cloud.auth/eventTypes/user.beforeSendEmail": "before-send-email",
17
+ "providers/cloud.auth/eventTypes/user.beforeSendSms": "before-send-sms",
14
18
  };
@@ -1,6 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AUTH_BLOCKING_EVENTS = exports.BEFORE_SIGN_IN_EVENT = exports.BEFORE_CREATE_EVENT = void 0;
3
+ exports.AUTH_BLOCKING_EVENTS = exports.BEFORE_SEND_SMS_EVENT = exports.BEFORE_SEND_EMAIL_EVENT = exports.BEFORE_SIGN_IN_EVENT = exports.BEFORE_CREATE_EVENT = void 0;
4
4
  exports.BEFORE_CREATE_EVENT = "providers/cloud.auth/eventTypes/user.beforeCreate";
5
5
  exports.BEFORE_SIGN_IN_EVENT = "providers/cloud.auth/eventTypes/user.beforeSignIn";
6
- exports.AUTH_BLOCKING_EVENTS = [exports.BEFORE_CREATE_EVENT, exports.BEFORE_SIGN_IN_EVENT];
6
+ exports.BEFORE_SEND_EMAIL_EVENT = "providers/cloud.auth/eventTypes/user.beforeSendEmail";
7
+ exports.BEFORE_SEND_SMS_EVENT = "providers/cloud.auth/eventTypes/user.beforeSendSms";
8
+ exports.AUTH_BLOCKING_EVENTS = [
9
+ exports.BEFORE_CREATE_EVENT,
10
+ exports.BEFORE_SIGN_IN_EVENT,
11
+ exports.BEFORE_SEND_EMAIL_EVENT,
12
+ exports.BEFORE_SEND_SMS_EVENT,
13
+ ];
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.interactiveExecuteQuery = exports.confirmDangerousQuery = void 0;
4
+ const ora = require("ora");
5
+ const clc = require("colorette");
6
+ const logger_1 = require("../../logger");
7
+ const prompt_1 = require("../../prompt");
8
+ const Table = require("cli-table");
9
+ const destructiveSqlKeywords = ["DROP", "DELETE"];
10
+ function checkIsDestructiveSql(query) {
11
+ const upperCaseQuery = query.toUpperCase();
12
+ return destructiveSqlKeywords.some((keyword) => upperCaseQuery.includes(keyword.toUpperCase()));
13
+ }
14
+ async function confirmDangerousQuery(query) {
15
+ if (checkIsDestructiveSql(query)) {
16
+ return await (0, prompt_1.confirm)({
17
+ message: clc.yellow("This query may be destructive. Are you sure you want to proceed?"),
18
+ default: false,
19
+ });
20
+ }
21
+ return true;
22
+ }
23
+ exports.confirmDangerousQuery = confirmDangerousQuery;
24
+ async function interactiveExecuteQuery(query, conn) {
25
+ const spinner = ora("Executing query...").start();
26
+ try {
27
+ const results = await conn.query(query);
28
+ spinner.succeed(clc.green("Query executed successfully"));
29
+ if (Array.isArray(results.rows) && results.rows.length > 0) {
30
+ const table = new Table({
31
+ head: Object.keys(results.rows[0]).map((key) => clc.cyan(key)),
32
+ style: { head: [], border: [] },
33
+ });
34
+ for (const row of results.rows) {
35
+ table.push(Object.values(row));
36
+ }
37
+ logger_1.logger.info(table.toString());
38
+ }
39
+ else {
40
+ if (query.toUpperCase().includes("SELECT")) {
41
+ logger_1.logger.info(clc.yellow("No results returned"));
42
+ }
43
+ }
44
+ }
45
+ catch (err) {
46
+ spinner.fail(clc.red(`Failed executing query: ${err}`));
47
+ }
48
+ }
49
+ exports.interactiveExecuteQuery = interactiveExecuteQuery;
@@ -62,7 +62,11 @@ async function deleteDocuments(project, docs, allowEmulator = false) {
62
62
  return { delete: doc.name };
63
63
  });
64
64
  const data = { writes };
65
- const res = await apiClient.post(url, data);
65
+ const res = await apiClient.post(url, data, {
66
+ retries: 10,
67
+ retryCodes: [429, 409, 503],
68
+ retryMaxTimeout: 20 * 1000,
69
+ });
66
70
  return res.body.writeResults.length;
67
71
  }
68
72
  exports.deleteDocuments = deleteDocuments;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.responseToError = void 0;
4
4
  const _ = require("lodash");
5
5
  const error_1 = require("./error");
6
- function responseToError(response, body) {
6
+ function responseToError(response, body, url) {
7
7
  if (response.statusCode < 400) {
8
8
  return;
9
9
  }
@@ -37,7 +37,10 @@ function responseToError(response, body) {
37
37
  message: errMessage,
38
38
  };
39
39
  }
40
- const message = "HTTP Error: " + response.statusCode + ", " + (body.error.message || body.error);
40
+ let message = "HTTP Error: " + response.statusCode + ", " + (body.error.message || body.error);
41
+ if (url) {
42
+ message = "Request to " + url + " had " + message;
43
+ }
41
44
  let exitCode;
42
45
  if (response.statusCode >= 500) {
43
46
  exitCode = 2;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.22.1",
3
+ "version": "13.23.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {