firebase-tools 13.7.5 → 13.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/lib/api.js +9 -1
  2. package/lib/apiv2.js +19 -13
  3. package/lib/apphosting/app.js +38 -58
  4. package/lib/apphosting/githubConnections.js +1 -1
  5. package/lib/apphosting/index.js +72 -20
  6. package/lib/checkValidTargetFilters.js +8 -1
  7. package/lib/commands/apphosting-backends-create.js +4 -3
  8. package/lib/commands/apphosting-backends-delete.js +24 -17
  9. package/lib/commands/apphosting-backends-list.js +3 -3
  10. package/lib/commands/apphosting-secrets-grantaccess.js +9 -5
  11. package/lib/commands/dataconnect-list.js +64 -0
  12. package/lib/commands/dataconnect-sdk-generate.js +37 -0
  13. package/lib/commands/dataconnect-sql-diff.js +25 -0
  14. package/lib/commands/dataconnect-sql-migrate.js +46 -0
  15. package/lib/commands/deploy.js +27 -1
  16. package/lib/commands/index.js +10 -0
  17. package/lib/commands/init.js +7 -0
  18. package/lib/commands/setup-emulators-dataconnect.js +12 -0
  19. package/lib/config.js +1 -0
  20. package/lib/dataconnect/build.js +23 -0
  21. package/lib/dataconnect/checkIam.js +30 -0
  22. package/lib/dataconnect/client.js +115 -0
  23. package/lib/dataconnect/dataplaneClient.js +16 -0
  24. package/lib/dataconnect/ensureApis.js +12 -0
  25. package/lib/dataconnect/fileUtils.js +89 -0
  26. package/lib/dataconnect/filters.js +45 -0
  27. package/lib/dataconnect/freeTrial.js +23 -0
  28. package/lib/dataconnect/graphqlError.js +13 -0
  29. package/lib/dataconnect/load.js +40 -0
  30. package/lib/dataconnect/names.js +48 -0
  31. package/lib/dataconnect/prompts.js +20 -0
  32. package/lib/dataconnect/provisionCloudSql.js +74 -0
  33. package/lib/dataconnect/schemaMigration.js +171 -0
  34. package/lib/dataconnect/types.js +23 -0
  35. package/lib/deploy/dataconnect/deploy.js +84 -0
  36. package/lib/deploy/dataconnect/index.js +9 -0
  37. package/lib/deploy/dataconnect/prepare.js +30 -0
  38. package/lib/deploy/dataconnect/release.js +65 -0
  39. package/lib/deploy/functions/checkIam.js +4 -34
  40. package/lib/deploy/functions/release/fabricator.js +7 -2
  41. package/lib/deploy/index.js +2 -0
  42. package/lib/downloadUtils.js +2 -2
  43. package/lib/emulator/constants.js +3 -0
  44. package/lib/emulator/controller.js +39 -12
  45. package/lib/emulator/dataconnectEmulator.js +88 -0
  46. package/lib/emulator/download.js +1 -1
  47. package/lib/emulator/downloadableEmulators.js +42 -3
  48. package/lib/emulator/portUtils.js +3 -3
  49. package/lib/emulator/registry.js +6 -1
  50. package/lib/emulator/types.js +3 -0
  51. package/lib/experiments.js +12 -5
  52. package/lib/extensions/emulator/specHelper.js +5 -39
  53. package/lib/frameworks/next/index.js +3 -1
  54. package/lib/frameworks/next/utils.js +1 -1
  55. package/lib/gcp/apphosting.js +6 -1
  56. package/lib/gcp/cloudsql/cloudsqladmin.js +155 -0
  57. package/lib/gcp/cloudsql/connect.js +128 -0
  58. package/lib/gcp/cloudsql/fbToolsAuthClient.js +42 -0
  59. package/lib/gcp/cloudsql/types.js +2 -0
  60. package/lib/gcp/firedata.js +26 -0
  61. package/lib/gcp/iam.js +33 -1
  62. package/lib/gcp/secretManager.js +1 -1
  63. package/lib/hosting/interactive.js +4 -0
  64. package/lib/init/features/dataconnect/index.js +160 -0
  65. package/lib/init/features/emulators.js +13 -0
  66. package/lib/init/features/functions/index.js +15 -3
  67. package/lib/init/features/index.js +3 -1
  68. package/lib/init/index.js +1 -0
  69. package/lib/logger.js +22 -2
  70. package/lib/operation-poller.js +8 -2
  71. package/lib/rc.js +10 -1
  72. package/lib/requireAuth.js +1 -0
  73. package/lib/requireTosAcceptance.js +21 -0
  74. package/lib/utils.js +55 -4
  75. package/package.json +6 -2
  76. package/schema/connector-yaml.json +54 -0
  77. package/schema/dataconnect-yaml.json +72 -0
  78. package/schema/firebase-config.json +103 -0
  79. package/templates/extensions/javascript/package.lint.json +2 -2
  80. package/templates/extensions/javascript/package.nolint.json +2 -2
  81. package/templates/extensions/typescript/package.lint.json +2 -2
  82. package/templates/extensions/typescript/package.nolint.json +2 -2
  83. package/templates/init/dataconnect/connector.yaml +2 -0
  84. package/templates/init/dataconnect/dataconnect.yaml +10 -0
  85. package/templates/init/dataconnect/mutations.gql +4 -0
  86. package/templates/init/dataconnect/queries.gql +6 -0
  87. package/templates/init/dataconnect/schema.gql +15 -0
  88. package/templates/init/functions/javascript/_gitignore +2 -1
  89. package/templates/init/functions/javascript/package.lint.json +2 -2
  90. package/templates/init/functions/javascript/package.nolint.json +2 -2
  91. package/templates/init/functions/python/_gitignore +1 -0
  92. package/templates/init/functions/typescript/_gitignore +1 -0
  93. package/templates/init/functions/typescript/package.lint.json +2 -2
  94. package/templates/init/functions/typescript/package.nolint.json +2 -2
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listUsers = exports.deleteUser = exports.getUser = exports.createUser = exports.createDatabase = exports.getDatabase = exports.listDatabases = exports.updateInstanceForDataConnect = exports.createInstance = exports.getInstance = exports.listInstances = void 0;
4
+ const apiv2_1 = require("../../apiv2");
5
+ const api_1 = require("../../api");
6
+ const operationPoller = require("../../operation-poller");
7
+ const API_VERSION = "v1";
8
+ const client = new apiv2_1.Client({
9
+ urlPrefix: (0, api_1.cloudSQLAdminOrigin)(),
10
+ auth: true,
11
+ apiVersion: API_VERSION,
12
+ });
13
+ async function listInstances(projectId) {
14
+ var _a;
15
+ const res = await client.get(`projects/${projectId}/instances`);
16
+ return (_a = res.body.items) !== null && _a !== void 0 ? _a : [];
17
+ }
18
+ exports.listInstances = listInstances;
19
+ async function getInstance(projectId, instanceId) {
20
+ const res = await client.get(`projects/${projectId}/instances/${instanceId}`);
21
+ return res.body;
22
+ }
23
+ exports.getInstance = getInstance;
24
+ async function createInstance(projectId, location, instanceId, enableGoogleMlIntegration) {
25
+ const databaseFlags = [{ name: "cloudsql.iam_authentication", value: "on" }];
26
+ if (enableGoogleMlIntegration) {
27
+ databaseFlags.push({ name: "cloudsql.enable_google_ml_integration", value: "on" });
28
+ }
29
+ const op = await client.post(`projects/${projectId}/instances`, {
30
+ name: instanceId,
31
+ region: location,
32
+ databaseVersion: "POSTGRES_15",
33
+ settings: {
34
+ tier: "db-f1-micro",
35
+ edition: "ENTERPRISE",
36
+ ipConfiguration: {
37
+ authorizedNetworks: [],
38
+ },
39
+ enableGoogleMlIntegration,
40
+ databaseFlags,
41
+ storageAutoResize: false,
42
+ userLabels: { "firebase-data-connect": "ft" },
43
+ insightsConfig: {
44
+ queryInsightsEnabled: true,
45
+ },
46
+ },
47
+ });
48
+ const opName = `projects/${projectId}/operations/${op.body.name}`;
49
+ const pollRes = await operationPoller.pollOperation({
50
+ apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
51
+ apiVersion: API_VERSION,
52
+ operationResourceName: opName,
53
+ doneFn: (op) => op.status === "DONE",
54
+ masterTimeout: 1200000,
55
+ });
56
+ return pollRes;
57
+ }
58
+ exports.createInstance = createInstance;
59
+ async function updateInstanceForDataConnect(instance, enableGoogleMlIntegration) {
60
+ let dbFlags = setDatabaseFlag({ name: "cloudsql.iam_authentication", value: "on" }, instance.settings.databaseFlags);
61
+ if (enableGoogleMlIntegration) {
62
+ dbFlags = setDatabaseFlag({ name: "cloudsql.enable_google_ml_integration", value: "on" }, dbFlags);
63
+ }
64
+ const op = await client.patch(`projects/${instance.project}/instances/${instance.name}`, {
65
+ settings: {
66
+ ipConfiguration: {
67
+ ipv4Enabled: true,
68
+ },
69
+ databaseFlags: dbFlags,
70
+ enableGoogleMlIntegration,
71
+ },
72
+ });
73
+ const opName = `projects/${instance.project}/operations/${op.body.name}`;
74
+ const pollRes = await operationPoller.pollOperation({
75
+ apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
76
+ apiVersion: API_VERSION,
77
+ operationResourceName: opName,
78
+ doneFn: (op) => op.status === "DONE",
79
+ masterTimeout: 1200000,
80
+ });
81
+ return pollRes;
82
+ }
83
+ exports.updateInstanceForDataConnect = updateInstanceForDataConnect;
84
+ function setDatabaseFlag(flag, flags = []) {
85
+ const temp = flags.filter((f) => f.name !== flag.name);
86
+ temp.push(flag);
87
+ return temp;
88
+ }
89
+ async function listDatabases(projectId, instanceId) {
90
+ const res = await client.get(`projects/${projectId}/instances/${instanceId}/databases`);
91
+ return res.body.items;
92
+ }
93
+ exports.listDatabases = listDatabases;
94
+ async function getDatabase(projectId, instanceId, databaseId) {
95
+ const res = await client.get(`projects/${projectId}/instances/${instanceId}/databases/${databaseId}`);
96
+ return res.body;
97
+ }
98
+ exports.getDatabase = getDatabase;
99
+ async function createDatabase(projectId, instanceId, databaseId) {
100
+ const op = await client.post(`projects/${projectId}/instances/${instanceId}/databases`, {
101
+ project: projectId,
102
+ instance: instanceId,
103
+ name: databaseId,
104
+ });
105
+ const opName = `projects/${projectId}/operations/${op.body.name}`;
106
+ const pollRes = await operationPoller.pollOperation({
107
+ apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
108
+ apiVersion: API_VERSION,
109
+ operationResourceName: opName,
110
+ doneFn: (op) => op.status === "DONE",
111
+ });
112
+ return pollRes;
113
+ }
114
+ exports.createDatabase = createDatabase;
115
+ async function createUser(projectId, instanceId, type, username, password) {
116
+ const op = await client.post(`projects/${projectId}/instances/${instanceId}/users`, {
117
+ name: username,
118
+ instance: instanceId,
119
+ project: projectId,
120
+ password: password,
121
+ sqlserverUserDetails: {
122
+ disabled: false,
123
+ serverRoles: ["cloudsqlsuperuser"],
124
+ },
125
+ type,
126
+ });
127
+ const opName = `projects/${projectId}/operations/${op.body.name}`;
128
+ const pollRes = await operationPoller.pollOperation({
129
+ apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
130
+ apiVersion: API_VERSION,
131
+ operationResourceName: opName,
132
+ doneFn: (op) => op.status === "DONE",
133
+ });
134
+ return pollRes;
135
+ }
136
+ exports.createUser = createUser;
137
+ async function getUser(projectId, instanceId, username) {
138
+ const res = await client.get(`projects/${projectId}/instances/${instanceId}/users/${username}`);
139
+ return res.body;
140
+ }
141
+ exports.getUser = getUser;
142
+ async function deleteUser(projectId, instanceId, username) {
143
+ const res = await client.delete(`projects/${projectId}/instances/${instanceId}/users`, {
144
+ queryParams: {
145
+ name: username,
146
+ },
147
+ });
148
+ return res.body;
149
+ }
150
+ exports.deleteUser = deleteUser;
151
+ async function listUsers(projectId, instanceId) {
152
+ const res = await client.get(`projects/${projectId}/instances/${instanceId}/users`);
153
+ return res.body.items;
154
+ }
155
+ exports.listUsers = listUsers;
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.firebaseowner = exports.setupIAMUser = exports.execute = void 0;
4
+ const pg = require("pg");
5
+ const cloud_sql_connector_1 = require("@google-cloud/cloud-sql-connector");
6
+ const requireAuth_1 = require("../../requireAuth");
7
+ const projectUtils_1 = require("../../projectUtils");
8
+ const cloudSqlAdminClient = require("./cloudsqladmin");
9
+ const utils = require("../../utils");
10
+ const logger_1 = require("../../logger");
11
+ const error_1 = require("../../error");
12
+ const fbToolsAuthClient_1 = require("./fbToolsAuthClient");
13
+ async function execute(sqlStatements, opts) {
14
+ const logFn = opts.silent ? logger_1.logger.debug : logger_1.logger.info;
15
+ const instance = await cloudSqlAdminClient.getInstance(opts.projectId, opts.instanceId);
16
+ const user = await cloudSqlAdminClient.getUser(opts.projectId, opts.instanceId, opts.username);
17
+ const connectionName = instance.connectionName;
18
+ if (!connectionName) {
19
+ throw new error_1.FirebaseError(`Could not get instance conection string for ${opts.instanceId}:${opts.databaseId}`);
20
+ }
21
+ let connector;
22
+ let pool;
23
+ switch (user.type) {
24
+ case "CLOUD_IAM_USER": {
25
+ connector = new cloud_sql_connector_1.Connector({
26
+ auth: new fbToolsAuthClient_1.FBToolsAuthClient(),
27
+ });
28
+ const clientOpts = await connector.getOptions({
29
+ instanceConnectionName: connectionName,
30
+ ipType: cloud_sql_connector_1.IpAddressTypes.PUBLIC,
31
+ authType: cloud_sql_connector_1.AuthTypes.IAM,
32
+ });
33
+ pool = new pg.Pool(Object.assign(Object.assign({}, clientOpts), { user: opts.username, database: opts.databaseId, max: 1 }));
34
+ break;
35
+ }
36
+ case "CLOUD_IAM_SERVICE_ACCOUNT": {
37
+ connector = new cloud_sql_connector_1.Connector();
38
+ const clientOpts = await connector.getOptions({
39
+ instanceConnectionName: connectionName,
40
+ ipType: cloud_sql_connector_1.IpAddressTypes.PUBLIC,
41
+ authType: cloud_sql_connector_1.AuthTypes.IAM,
42
+ });
43
+ pool = new pg.Pool(Object.assign(Object.assign({}, clientOpts), { user: opts.username, database: opts.databaseId, max: 1 }));
44
+ break;
45
+ }
46
+ default: {
47
+ if (!opts.password) {
48
+ throw new error_1.FirebaseError(`Cannot connect as BUILT_IN user without a password.`);
49
+ }
50
+ connector = new cloud_sql_connector_1.Connector({
51
+ auth: new fbToolsAuthClient_1.FBToolsAuthClient(),
52
+ });
53
+ const clientOpts = await connector.getOptions({
54
+ instanceConnectionName: connectionName,
55
+ ipType: cloud_sql_connector_1.IpAddressTypes.PUBLIC,
56
+ });
57
+ pool = new pg.Pool(Object.assign(Object.assign({}, clientOpts), { user: opts.username, password: opts.password, database: opts.databaseId, max: 1 }));
58
+ break;
59
+ }
60
+ }
61
+ for (const s of sqlStatements) {
62
+ logFn(`Executing: '${s}' as ${opts.username}`);
63
+ try {
64
+ await pool.query(s);
65
+ }
66
+ catch (err) {
67
+ throw new error_1.FirebaseError(`Error executing ${err}`);
68
+ }
69
+ }
70
+ await pool.end();
71
+ connector.close();
72
+ }
73
+ exports.execute = execute;
74
+ async function setupIAMUser(instanceId, databaseId, options) {
75
+ const projectId = (0, projectUtils_1.needProjectId)(options);
76
+ const account = await (0, requireAuth_1.requireAuth)(options);
77
+ if (!account) {
78
+ throw new error_1.FirebaseError("No account to set up! Run `firebase login` or set Application Default Credentials");
79
+ }
80
+ const setupUser = "firebasesuperuser";
81
+ const temporaryPassword = utils.generateId(20);
82
+ await cloudSqlAdminClient.createUser(projectId, instanceId, "BUILT_IN", setupUser, temporaryPassword);
83
+ const { user, mode } = toDatabaseUser(account);
84
+ await cloudSqlAdminClient.createUser(projectId, instanceId, mode, user);
85
+ const grants = [
86
+ `do
87
+ $$
88
+ begin
89
+ if not exists (select FROM pg_catalog.pg_roles
90
+ WHERE rolname = '${firebaseowner(databaseId)}') then
91
+ CREATE ROLE "${firebaseowner(databaseId)}" WITH ADMIN "${setupUser}";
92
+ end if;
93
+ end
94
+ $$
95
+ ;`,
96
+ `GRANT ALL PRIVILEGES ON DATABASE "${databaseId}" TO "${firebaseowner(databaseId)}"`,
97
+ `GRANT cloudsqlsuperuser TO "${firebaseowner(databaseId)}"`,
98
+ `GRANT "${firebaseowner(databaseId)}" TO "${setupUser}"`,
99
+ `GRANT "${firebaseowner(databaseId)}" TO "${user}"`,
100
+ `ALTER SCHEMA public OWNER TO "${firebaseowner(databaseId)}"`,
101
+ `GRANT USAGE ON SCHEMA "public" TO PUBLIC`,
102
+ `GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA "public" TO PUBLIC`,
103
+ `GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA "public" TO PUBLIC`,
104
+ ];
105
+ await execute(grants, {
106
+ projectId,
107
+ instanceId,
108
+ databaseId,
109
+ username: setupUser,
110
+ password: temporaryPassword,
111
+ silent: true,
112
+ });
113
+ return user;
114
+ }
115
+ exports.setupIAMUser = setupIAMUser;
116
+ function firebaseowner(databaseId) {
117
+ return `firebaseowner_${databaseId}_public`;
118
+ }
119
+ exports.firebaseowner = firebaseowner;
120
+ function toDatabaseUser(account) {
121
+ let mode = "CLOUD_IAM_USER";
122
+ let user = account;
123
+ if (account.endsWith(".gserviceaccount.com")) {
124
+ user = account.replace(".gserviceaccount.com", "");
125
+ mode = "CLOUD_IAM_SERVICE_ACCOUNT";
126
+ }
127
+ return { user, mode };
128
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FBToolsAuthClient = void 0;
4
+ const google_auth_library_1 = require("google-auth-library");
5
+ const apiv2 = require("../../apiv2");
6
+ const error_1 = require("../../error");
7
+ class FBToolsAuthClient extends google_auth_library_1.AuthClient {
8
+ async request(opts) {
9
+ var _a;
10
+ if (!opts.url) {
11
+ throw new error_1.FirebaseError("opts.url was undefined");
12
+ }
13
+ const url = new URL(opts.url);
14
+ const client = new apiv2.Client({
15
+ urlPrefix: url.origin,
16
+ auth: true,
17
+ });
18
+ const res = await client.request({
19
+ method: (_a = opts.method) !== null && _a !== void 0 ? _a : "POST",
20
+ path: url.pathname,
21
+ queryParams: opts.params,
22
+ body: opts.data,
23
+ responseType: opts.responseType,
24
+ });
25
+ return {
26
+ config: opts,
27
+ status: res.status,
28
+ statusText: res.response.statusText,
29
+ data: res.body,
30
+ headers: res.response.headers,
31
+ request: {},
32
+ };
33
+ }
34
+ async getAccessToken() {
35
+ return { token: await apiv2.getAccessToken() };
36
+ }
37
+ async getRequestHeaders() {
38
+ const token = await this.getAccessToken();
39
+ return Object.assign(Object.assign({}, apiv2.STANDARD_HEADERS), { Authorization: `Bearer ${token.token}` });
40
+ }
41
+ }
42
+ exports.FBToolsAuthClient = FBToolsAuthClient;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isProductTosAccepted = exports.getAcceptanceStatus = exports.getTosStatus = exports.APP_CHECK_TOS_ID = exports.APPHOSTING_TOS_ID = void 0;
4
+ const apiv2_1 = require("../apiv2");
5
+ const api_1 = require("../api");
6
+ const error_1 = require("../error");
7
+ const client = new apiv2_1.Client({ urlPrefix: (0, api_1.firedataOrigin)(), auth: true, apiVersion: "v1" });
8
+ exports.APPHOSTING_TOS_ID = "APP_HOSTING_TOS";
9
+ exports.APP_CHECK_TOS_ID = "APP_CHECK";
10
+ async function getTosStatus() {
11
+ const res = await client.get("accessmanagement/tos:getStatus");
12
+ return res.body;
13
+ }
14
+ exports.getTosStatus = getTosStatus;
15
+ function getAcceptanceStatus(response, tosId) {
16
+ const perServiceStatus = response.perServiceStatus.find((tosStatus) => tosStatus.tosId === tosId);
17
+ if (perServiceStatus === undefined) {
18
+ throw new error_1.FirebaseError(`Missing terms of service status for product: ${tosId}`);
19
+ }
20
+ return perServiceStatus.serviceStatus.status;
21
+ }
22
+ exports.getAcceptanceStatus = getAcceptanceStatus;
23
+ function isProductTosAccepted(response, tosId) {
24
+ return getAcceptanceStatus(response, tosId) === "ACCEPTED";
25
+ }
26
+ exports.isProductTosAccepted = isProductTosAccepted;
package/lib/gcp/iam.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.testIamPermissions = exports.testResourceIamPermissions = exports.getRole = exports.listServiceAccountKeys = exports.deleteServiceAccount = exports.createServiceAccountKey = exports.getServiceAccount = exports.createServiceAccount = exports.getDefaultComputeEngineServiceAgent = exports.getDefaultCloudBuildServiceAgent = void 0;
3
+ exports.printManualIamConfig = exports.mergeBindings = exports.testIamPermissions = exports.testResourceIamPermissions = exports.getRole = exports.listServiceAccountKeys = exports.deleteServiceAccount = exports.createServiceAccountKey = exports.getServiceAccount = exports.createServiceAccount = exports.getDefaultComputeEngineServiceAgent = exports.getDefaultCloudBuildServiceAgent = void 0;
4
4
  const api_1 = require("../api");
5
5
  const logger_1 = require("../logger");
6
6
  const apiv2_1 = require("../apiv2");
7
+ const utils = require("../utils");
7
8
  const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.iamOrigin)(), apiVersion: "v1" });
8
9
  function getDefaultCloudBuildServiceAgent(projectNumber) {
9
10
  return `${projectNumber}@cloudbuild.gserviceaccount.com`;
@@ -82,3 +83,34 @@ async function testIamPermissions(projectId, permissions) {
82
83
  return testResourceIamPermissions((0, api_1.resourceManagerOrigin)(), "v1", `projects/${projectId}`, permissions, `projects/${projectId}`);
83
84
  }
84
85
  exports.testIamPermissions = testIamPermissions;
86
+ function mergeBindings(policy, requiredBindings) {
87
+ let updated = false;
88
+ for (const requiredBinding of requiredBindings) {
89
+ const match = policy.bindings.find((b) => b.role === requiredBinding.role);
90
+ if (!match) {
91
+ updated = true;
92
+ policy.bindings.push(requiredBinding);
93
+ continue;
94
+ }
95
+ for (const requiredMember of requiredBinding.members) {
96
+ if (!match.members.find((m) => m === requiredMember)) {
97
+ updated = true;
98
+ match.members.push(requiredMember);
99
+ }
100
+ }
101
+ }
102
+ return updated;
103
+ }
104
+ exports.mergeBindings = mergeBindings;
105
+ function printManualIamConfig(requiredBindings, projectId, prefix) {
106
+ utils.logLabeledBullet(prefix, "Failed to verify the project has the correct IAM bindings for a successful deployment.", "warn");
107
+ utils.logLabeledBullet(prefix, "You can either re-run this command as a project owner or manually run the following set of `gcloud` commands:", "warn");
108
+ for (const binding of requiredBindings) {
109
+ for (const member of binding.members) {
110
+ utils.logLabeledBullet(prefix, `\`gcloud projects add-iam-policy-binding ${projectId} ` +
111
+ `--member=${member} ` +
112
+ `--role=${binding.role}\``, "warn");
113
+ }
114
+ }
115
+ }
116
+ exports.printManualIamConfig = printManualIamConfig;
@@ -51,7 +51,7 @@ async function getSecretMetadata(projectId, secretName, version) {
51
51
  const secretInfo = {};
52
52
  try {
53
53
  secretInfo.secret = await getSecret(projectId, secretName);
54
- secretInfo.secretVersion = getSecretVersion(projectId, secretName, version);
54
+ secretInfo.secretVersion = await getSecretVersion(projectId, secretName, version);
55
55
  }
56
56
  catch (err) {
57
57
  if (err.status !== 404) {
@@ -43,6 +43,7 @@ async function interactiveCreateHostingSite(siteId, appId, options) {
43
43
  if (options.nonInteractive) {
44
44
  throw err;
45
45
  }
46
+ id = "";
46
47
  suggestion = getSuggestionFromError(err);
47
48
  }
48
49
  }
@@ -71,5 +72,8 @@ function getSuggestionFromError(err) {
71
72
  return match[1];
72
73
  }
73
74
  }
75
+ else {
76
+ (0, utils_1.logWarning)(err.message);
77
+ }
74
78
  return;
75
79
  }
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doSetup = void 0;
4
+ const path_1 = require("path");
5
+ const prompt_1 = require("../../../prompt");
6
+ const fs_1 = require("fs");
7
+ const provisionCloudSql_1 = require("../../../dataconnect/provisionCloudSql");
8
+ const cloudsql = require("../../../gcp/cloudsql/cloudsqladmin");
9
+ const ensureApis_1 = require("../../../dataconnect/ensureApis");
10
+ const client_1 = require("../../../dataconnect/client");
11
+ const TEMPLATE_ROOT = (0, path_1.resolve)(__dirname, "../../../../templates/init/dataconnect/");
12
+ const DATACONNECT_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "dataconnect.yaml"), "utf8");
13
+ const CONNECTOR_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "connector.yaml"), "utf8");
14
+ const SCHEMA_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "schema.gql"), "utf8");
15
+ const QUERIES_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "queries.gql"), "utf8");
16
+ const MUTATIONS_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "mutations.gql"), "utf8");
17
+ async function doSetup(setup, config) {
18
+ var _a, _b, _c;
19
+ if (setup.projectId) {
20
+ await (0, ensureApis_1.ensureApis)(setup.projectId);
21
+ }
22
+ const serviceId = await (0, prompt_1.promptOnce)({
23
+ message: "What ID would you like to use for this service?",
24
+ type: "input",
25
+ default: "dataconnect",
26
+ });
27
+ let locationOptions = [
28
+ { name: "us-central1", value: "us-central1" },
29
+ { name: "europe-north1", value: "europe-north1" },
30
+ { name: "europe-central2", value: "europe-central2" },
31
+ { name: "europe-west1", value: "europe-west1" },
32
+ { name: "southamerica-west1", value: "southamerica-west1" },
33
+ { name: "us-east4", value: "us-east4" },
34
+ { name: "us-west1", value: "us-west1" },
35
+ { name: "asia-southeast1", value: "asia-southeast1" },
36
+ ];
37
+ if (setup.projectId) {
38
+ const locations = await (0, client_1.listLocations)(setup.projectId);
39
+ locationOptions = locations.map((l) => {
40
+ return { name: l, value: l };
41
+ });
42
+ }
43
+ const locationId = await (0, prompt_1.promptOnce)({
44
+ message: "What location would you like to deploy this service into?",
45
+ type: "list",
46
+ choices: locationOptions,
47
+ });
48
+ const connectorId = await (0, prompt_1.promptOnce)({
49
+ message: "What ID would you like to use for your connector?",
50
+ type: "input",
51
+ default: "my-connector",
52
+ });
53
+ const dir = config.get("dataconnect.source") || "dataconnect";
54
+ if (!config.has("dataconnect")) {
55
+ config.set("dataconnect.source", dir);
56
+ config.set("dataconnect.location", locationId);
57
+ }
58
+ let cloudSqlInstanceId = "";
59
+ let newInstance = false;
60
+ if (setup.projectId) {
61
+ const instances = await cloudsql.listInstances(setup.projectId);
62
+ const instancesInLocation = instances.filter((i) => i.region === locationId);
63
+ const choices = instancesInLocation.map((i) => {
64
+ return { name: i.name, value: i.name };
65
+ });
66
+ choices.push({ name: "Create a new instance", value: "" });
67
+ if (instancesInLocation.length) {
68
+ cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
69
+ message: `Which CloudSSQL in ${locationId} would you like to use?`,
70
+ type: "list",
71
+ choices,
72
+ });
73
+ }
74
+ }
75
+ if (cloudSqlInstanceId === "") {
76
+ newInstance = true;
77
+ cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
78
+ message: `What ID would you like to use for your new CloudSQL instance?`,
79
+ type: "input",
80
+ default: `dataconnect-test`,
81
+ });
82
+ }
83
+ let cloudSqlDatabase = "";
84
+ let newDB = false;
85
+ if (!newInstance && setup.projectId) {
86
+ const dbs = await cloudsql.listDatabases(setup.projectId, cloudSqlInstanceId);
87
+ const choices = dbs.map((d) => {
88
+ return { name: d.name, value: d.name };
89
+ });
90
+ choices.push({ name: "Create a new database", value: "" });
91
+ if (dbs.length) {
92
+ cloudSqlDatabase = await (0, prompt_1.promptOnce)({
93
+ message: `Which database in ${cloudSqlInstanceId} would you like to use?`,
94
+ type: "list",
95
+ choices,
96
+ });
97
+ }
98
+ }
99
+ if (cloudSqlDatabase === "") {
100
+ newDB = true;
101
+ cloudSqlDatabase = await (0, prompt_1.promptOnce)({
102
+ message: `What ID would you like to use for your new database in ${cloudSqlInstanceId}?`,
103
+ type: "input",
104
+ default: `dataconnect`,
105
+ });
106
+ }
107
+ const defaultConnectionString = (_c = (_b = (_a = setup.rcfile.dataconnectEmulatorConfig) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.localConnectionString) !== null && _c !== void 0 ? _c : "postgresql://localhost:5432?sslmode=disable";
108
+ const localConnectionString = await (0, prompt_1.promptOnce)({
109
+ type: "input",
110
+ name: "localConnectionString",
111
+ message: `What is the connection string of the local Postgres instance you would like to use with the Data Connect emulator?`,
112
+ default: defaultConnectionString,
113
+ });
114
+ setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
115
+ const subbedDataconnectYaml = subValues(DATACONNECT_YAML_TEMPLATE, {
116
+ serviceId,
117
+ cloudSqlInstanceId,
118
+ cloudSqlDatabase,
119
+ connectorId,
120
+ });
121
+ const subbedConnectorYaml = subValues(CONNECTOR_YAML_TEMPLATE, {
122
+ serviceId,
123
+ cloudSqlInstanceId,
124
+ cloudSqlDatabase,
125
+ connectorId,
126
+ });
127
+ await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
128
+ await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "connector.yaml"), subbedConnectorYaml);
129
+ await config.askWriteProjectFile((0, path_1.join)(dir, "schema", "schema.gql"), SCHEMA_TEMPLATE);
130
+ await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "queries.gql"), QUERIES_TEMPLATE);
131
+ await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "mutations.gql"), MUTATIONS_TEMPLATE);
132
+ if (setup.projectId &&
133
+ (newInstance || newDB) &&
134
+ (await (0, prompt_1.confirm)({
135
+ message: "Would you like to provision your CloudSQL instance and database now? This will take a few minutes.",
136
+ default: true,
137
+ }))) {
138
+ await (0, provisionCloudSql_1.provisionCloudSql)({
139
+ projectId: setup.projectId,
140
+ locationId,
141
+ instanceId: cloudSqlInstanceId,
142
+ databaseId: cloudSqlDatabase,
143
+ enableGoogleMlIntegration: false,
144
+ });
145
+ }
146
+ }
147
+ exports.doSetup = doSetup;
148
+ function subValues(template, replacementValues) {
149
+ const replacements = {
150
+ serviceId: "__serviceId__",
151
+ cloudSqlDatabase: "__cloudSqlDatabase__",
152
+ cloudSqlInstanceId: "__cloudSqlInstanceId__",
153
+ connectorId: "__connectorId__",
154
+ };
155
+ let replaced = template;
156
+ for (const [k, v] of Object.entries(replacementValues)) {
157
+ replaced = replaced.replace(replacements[k], v);
158
+ }
159
+ return replaced;
160
+ }
@@ -9,6 +9,7 @@ const types_1 = require("../../emulator/types");
9
9
  const constants_1 = require("../../emulator/constants");
10
10
  const downloadableEmulators_1 = require("../../emulator/downloadableEmulators");
11
11
  async function doSetup(setup, config) {
12
+ var _a, _b, _c;
12
13
  const choices = types_1.ALL_SERVICE_EMULATORS.map((e) => {
13
14
  return {
14
15
  value: e,
@@ -76,6 +77,18 @@ async function doSetup(setup, config) {
76
77
  ui.port = isNaN(portNum) ? undefined : portNum;
77
78
  }
78
79
  }
80
+ if (selections.emulators.includes(types_1.Emulators.DATACONNECT)) {
81
+ const defaultConnectionString = (_c = (_b = (_a = setup.rcfile.dataconnectEmulatorConfig) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.localConnectionString) !== null && _c !== void 0 ? _c : "postgresql://localhost:5432";
82
+ const localConnectionString = await (0, prompt_1.prompt)(setup.config.emulators[types_1.Emulators.DATACONNECT], [
83
+ {
84
+ type: "input",
85
+ name: "localConnectionString",
86
+ message: `What is the connection string of the local Postgres instance you would like to use with the Data Connect emulator?`,
87
+ default: defaultConnectionString,
88
+ },
89
+ ]);
90
+ setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
91
+ }
79
92
  await (0, prompt_1.prompt)(selections, [
80
93
  {
81
94
  name: "download",
@@ -153,13 +153,25 @@ async function languageSetup(setup, config) {
153
153
  const cbconfig = (0, projectConfig_1.configForCodebase)(setup.config.functions, setup.functions.codebase);
154
154
  switch (language) {
155
155
  case "javascript":
156
- cbconfig.ignore = ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log"];
156
+ cbconfig.ignore = [
157
+ "node_modules",
158
+ ".git",
159
+ "firebase-debug.log",
160
+ "firebase-debug.*.log",
161
+ "*.local",
162
+ ];
157
163
  break;
158
164
  case "typescript":
159
- cbconfig.ignore = ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log"];
165
+ cbconfig.ignore = [
166
+ "node_modules",
167
+ ".git",
168
+ "firebase-debug.log",
169
+ "firebase-debug.*.log",
170
+ "*.local",
171
+ ];
160
172
  break;
161
173
  case "python":
162
- cbconfig.ignore = ["venv", ".git", "firebase-debug.log", "firebase-debug.*.log"];
174
+ cbconfig.ignore = ["venv", ".git", "firebase-debug.log", "firebase-debug.*.log", "*.local"];
163
175
  break;
164
176
  }
165
177
  return require("./" + language).setup(setup, config);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.apphosting = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storage = exports.hosting = exports.functions = exports.firestore = exports.database = exports.account = void 0;
3
+ exports.apphosting = exports.dataconnect = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storage = exports.hosting = exports.functions = exports.firestore = exports.database = exports.account = void 0;
4
4
  var account_1 = require("./account");
5
5
  Object.defineProperty(exports, "account", { enumerable: true, get: function () { return account_1.doSetup; } });
6
6
  var database_1 = require("./database");
@@ -23,5 +23,7 @@ var remoteconfig_1 = require("./remoteconfig");
23
23
  Object.defineProperty(exports, "remoteconfig", { enumerable: true, get: function () { return remoteconfig_1.doSetup; } });
24
24
  var github_1 = require("./hosting/github");
25
25
  Object.defineProperty(exports, "hostingGithub", { enumerable: true, get: function () { return github_1.initGitHub; } });
26
+ var dataconnect_1 = require("./dataconnect");
27
+ Object.defineProperty(exports, "dataconnect", { enumerable: true, get: function () { return dataconnect_1.doSetup; } });
26
28
  var apphosting_1 = require("../../apphosting");
27
29
  Object.defineProperty(exports, "apphosting", { enumerable: true, get: function () { return apphosting_1.doSetup; } });