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,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const path = require("path");
5
+ const command_1 = require("../command");
6
+ const dataconnectEmulator_1 = require("../emulator/dataconnectEmulator");
7
+ const projectUtils_1 = require("../projectUtils");
8
+ const load_1 = require("../dataconnect/load");
9
+ const fileUtils_1 = require("../dataconnect/fileUtils");
10
+ const logger_1 = require("../logger");
11
+ exports.command = new command_1.Command("dataconnect:sdk:generate")
12
+ .description("generates typed SDKs for your Data Connect connectors")
13
+ .action(async (options) => {
14
+ const projectId = (0, projectUtils_1.needProjectId)(options);
15
+ const services = (0, fileUtils_1.readFirebaseJson)(options.config);
16
+ for (const service of services) {
17
+ let configDir = service.source;
18
+ if (!path.isAbsolute(configDir)) {
19
+ const cwd = options.cwd || process.cwd();
20
+ configDir = path.resolve(path.join(cwd), configDir);
21
+ }
22
+ const serviceInfo = await (0, load_1.load)(projectId, service.location, configDir);
23
+ const args = {
24
+ projectId,
25
+ configDir,
26
+ auto_download: true,
27
+ rc: options.rc,
28
+ locationId: service.location,
29
+ };
30
+ const dataconnectEmulator = new dataconnectEmulator_1.DataConnectEmulator(args);
31
+ for (const conn of serviceInfo.connectorInfo) {
32
+ const output = await dataconnectEmulator.generate(conn.connectorYaml.connectorId);
33
+ logger_1.logger.info(output);
34
+ logger_1.logger.info(`Generated SDKs for ${conn.connectorYaml.connectorId}`);
35
+ }
36
+ }
37
+ });
@@ -0,0 +1,25 @@
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 ensureApis_1 = require("../dataconnect/ensureApis");
7
+ const requirePermissions_1 = require("../requirePermissions");
8
+ const fileUtils_1 = require("../dataconnect/fileUtils");
9
+ const schemaMigration_1 = require("../dataconnect/schemaMigration");
10
+ const requireAuth_1 = require("../requireAuth");
11
+ exports.command = new command_1.Command("dataconnect:sql:diff [serviceId]")
12
+ .description("displays the differences between a local DataConnect schema and your CloudSQL database's current schema")
13
+ .before(requirePermissions_1.requirePermissions, [
14
+ "firebasedataconnect.services.list",
15
+ "firebasedataconnect.schemas.list",
16
+ "firebasedataconnect.schemas.update",
17
+ ])
18
+ .before(requireAuth_1.requireAuth)
19
+ .action(async (serviceId, options) => {
20
+ const projectId = (0, projectUtils_1.needProjectId)(options);
21
+ await (0, ensureApis_1.ensureApis)(projectId);
22
+ const serviceInfo = await (0, fileUtils_1.pickService)(projectId, options.config, serviceId);
23
+ const diffs = await (0, schemaMigration_1.diffSchema)(serviceInfo.schema);
24
+ return { projectId, serviceId, diffs };
25
+ });
@@ -0,0 +1,46 @@
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 fileUtils_1 = require("../dataconnect/fileUtils");
7
+ const logger_1 = require("../logger");
8
+ const error_1 = require("../error");
9
+ const schemaMigration_1 = require("../dataconnect/schemaMigration");
10
+ const requireAuth_1 = require("../requireAuth");
11
+ const requirePermissions_1 = require("../requirePermissions");
12
+ const ensureApis_1 = require("../dataconnect/ensureApis");
13
+ exports.command = new command_1.Command("dataconnect:sql:migrate [serviceId]")
14
+ .description("migrates your CloudSQL database's schema to match your local DataConnect schema")
15
+ .before(requirePermissions_1.requirePermissions, [
16
+ "firebasedataconnect.services.list",
17
+ "firebasedataconnect.schemas.list",
18
+ "firebasedataconnect.schemas.update",
19
+ "cloudsql.instances.connect",
20
+ "cloudsql.users.create",
21
+ ])
22
+ .before(requireAuth_1.requireAuth)
23
+ .withForce("Execute any required database changes without prompting")
24
+ .action(async (serviceId, options) => {
25
+ var _a;
26
+ const projectId = (0, projectUtils_1.needProjectId)(options);
27
+ await (0, ensureApis_1.ensureApis)(projectId);
28
+ const serviceInfo = await (0, fileUtils_1.pickService)(projectId, options.config, serviceId);
29
+ const instanceId = (_a = serviceInfo.dataConnectYaml.schema.datasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instanceId;
30
+ if (!instanceId) {
31
+ throw new error_1.FirebaseError("dataconnect.yaml is missing field schema.datasource.postgresql.cloudsql.instanceId");
32
+ }
33
+ const diffs = await (0, schemaMigration_1.migrateSchema)({
34
+ options,
35
+ schema: serviceInfo.schema,
36
+ allowNonInteractiveMigration: true,
37
+ validateOnly: true,
38
+ });
39
+ if (diffs.length) {
40
+ logger_1.logger.info(`Schema sucessfully migrated! Run 'firebase deploy' to deploy your new schema to your Data Connect service.`);
41
+ }
42
+ else {
43
+ logger_1.logger.info("Schema was already up to date!");
44
+ }
45
+ return { projectId, serviceId, diffs };
46
+ });
@@ -23,6 +23,7 @@ exports.VALID_DEPLOY_TARGETS = [
23
23
  "hosting",
24
24
  "remoteconfig",
25
25
  "extensions",
26
+ "dataconnect",
26
27
  ];
27
28
  exports.TARGET_PERMISSIONS = {
28
29
  database: ["firebasedatabase.instances.update"],
@@ -47,6 +48,29 @@ exports.TARGET_PERMISSIONS = {
47
48
  "firebaserules.releases.update",
48
49
  ],
49
50
  remoteconfig: ["cloudconfig.configs.get", "cloudconfig.configs.update"],
51
+ dataconnect: [
52
+ "cloudsql.databases.create",
53
+ "cloudsql.databases.update",
54
+ "cloudsql.instances.connect",
55
+ "cloudsql.instances.create",
56
+ "cloudsql.instances.get",
57
+ "cloudsql.instances.list",
58
+ "cloudsql.instances.update",
59
+ "cloudsql.users.create",
60
+ "firebasedataconnect.connectors.create",
61
+ "firebasedataconnect.connectors.delete",
62
+ "firebasedataconnect.connectors.list",
63
+ "firebasedataconnect.connectors.update",
64
+ "firebasedataconnect.operations.get",
65
+ "firebasedataconnect.services.create",
66
+ "firebasedataconnect.services.delete",
67
+ "firebasedataconnect.services.update",
68
+ "firebasedataconnect.services.list",
69
+ "firebasedataconnect.schemas.create",
70
+ "firebasedataconnect.schemas.delete",
71
+ "firebasedataconnect.schemas.list",
72
+ "firebasedataconnect.schemas.update",
73
+ ],
50
74
  };
51
75
  exports.command = new command_1.Command("deploy")
52
76
  .description("deploy code and assets to your Firebase project")
@@ -56,7 +80,9 @@ exports.command = new command_1.Command("deploy")
56
80
  .option("--only <targets>", 'only deploy to specified, comma-separated targets (e.g. "hosting,storage"). For functions, ' +
57
81
  'can specify filters with colons to scope function deploys to only those functions (e.g. "--only functions:func1,functions:func2"). ' +
58
82
  "When filtering based on export groups (the exported module object keys), use dots to specify group names " +
59
- '(e.g. "--only functions:group1.subgroup1,functions:group2)"')
83
+ '(e.g. "--only functions:group1.subgroup1,functions:group2)"' +
84
+ "For data connect, can specify filters with colons to deploy only a service, connector, or schema" +
85
+ '(e.g. "--only dataconnect:serviceId,dataconnect:serviceId:connectorId,dataconnect:serviceId:schema"). ')
60
86
  .option("--except <targets>", 'deploy to all targets except specified (e.g. "database")')
61
87
  .before(requireConfig_1.requireConfig)
62
88
  .before((options) => {
@@ -200,6 +200,16 @@ function load(client) {
200
200
  client.setup.emulators.pubsub = loadCommand("setup-emulators-pubsub");
201
201
  client.setup.emulators.storage = loadCommand("setup-emulators-storage");
202
202
  client.setup.emulators.ui = loadCommand("setup-emulators-ui");
203
+ if (experiments.isEnabled("dataconnect")) {
204
+ client.dataconnect = {};
205
+ client.setup.emulators.dataconnect = loadCommand("setup-emulators-dataconnect");
206
+ client.dataconnect.list = loadCommand("dataconnect-list");
207
+ client.dataconnect.sql = {};
208
+ client.dataconnect.sql.diff = loadCommand("dataconnect-sql-diff");
209
+ client.dataconnect.sql.migrate = loadCommand("dataconnect-sql-migrate");
210
+ client.dataconnect.sdk = {};
211
+ client.dataconnect.sdk.generate = loadCommand("dataconnect-sdk-generate");
212
+ }
203
213
  client.target = loadCommand("target");
204
214
  client.target.apply = loadCommand("target-apply");
205
215
  client.target.clear = loadCommand("target-clear");
@@ -76,6 +76,13 @@ if ((0, experiments_1.isEnabled)("apphosting")) {
76
76
  checked: false,
77
77
  });
78
78
  }
79
+ if ((0, experiments_1.isEnabled)("dataconnect")) {
80
+ choices.push({
81
+ value: "dataconnect",
82
+ name: "Data Connect: Set up a Firebase Data Connect service.",
83
+ checked: false,
84
+ });
85
+ }
79
86
  const featureNames = choices.map((choice) => choice.value);
80
87
  const DESCRIPTION = `Interactively configure the current directory as a Firebase project or initialize new features in an already configured Firebase project directory.
81
88
 
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const download_1 = require("../emulator/download");
6
+ const types_1 = require("../emulator/types");
7
+ const NAME = types_1.Emulators.DATACONNECT;
8
+ exports.command = new command_1.Command(`setup:emulators:${NAME}`)
9
+ .description(`downloads the ${NAME} emulator`)
10
+ .action(() => {
11
+ return (0, download_1.downloadEmulator)(NAME);
12
+ });
package/lib/config.js CHANGED
@@ -226,4 +226,5 @@ Config.MATERIALIZE_TARGETS = [
226
226
  "hosting",
227
227
  "storage",
228
228
  "remoteconfig",
229
+ "dataconnect",
229
230
  ];
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.build = void 0;
4
+ const dataconnectEmulator_1 = require("../emulator/dataconnectEmulator");
5
+ const error_1 = require("../error");
6
+ const graphqlError_1 = require("./graphqlError");
7
+ async function build(options, configDir) {
8
+ var _a, _b, _c;
9
+ const projectId = (_a = options.project) !== null && _a !== void 0 ? _a : "demo-test";
10
+ const args = {
11
+ projectId,
12
+ configDir,
13
+ auto_download: true,
14
+ rc: options.rc,
15
+ };
16
+ const dataconnectEmulator = new dataconnectEmulator_1.DataConnectEmulator(args);
17
+ const buildResult = await dataconnectEmulator.build();
18
+ if ((_b = buildResult === null || buildResult === void 0 ? void 0 : buildResult.errors) === null || _b === void 0 ? void 0 : _b.length) {
19
+ throw new error_1.FirebaseError(`There are errors in your schema and connector files:\n${buildResult.errors.map(graphqlError_1.prettify).join("\n")}`);
20
+ }
21
+ return (_c = buildResult === null || buildResult === void 0 ? void 0 : buildResult.metadata) !== null && _c !== void 0 ? _c : {};
22
+ }
23
+ exports.build = build;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.grantRolesToCloudSqlServiceAccount = void 0;
4
+ const iam = require("../gcp/iam");
5
+ const resourceManager_1 = require("../gcp/resourceManager");
6
+ const cloudSqlAdmin = require("../gcp/cloudsql/cloudsqladmin");
7
+ const error_1 = require("../error");
8
+ async function grantRolesToCloudSqlServiceAccount(projectId, instanceId, roles) {
9
+ const instance = await cloudSqlAdmin.getInstance(projectId, instanceId);
10
+ const saEmail = instance.serviceAccountEmailAddress;
11
+ const policy = await (0, resourceManager_1.getIamPolicy)(projectId);
12
+ const requiredBindings = roles.map((r) => {
13
+ const binding = {
14
+ role: r,
15
+ members: [`serviceAccount:${saEmail}`],
16
+ };
17
+ return binding;
18
+ });
19
+ const updated = iam.mergeBindings(policy, requiredBindings);
20
+ if (updated) {
21
+ try {
22
+ await (0, resourceManager_1.setIamPolicy)(projectId, policy, "bindings");
23
+ }
24
+ catch (err) {
25
+ iam.printManualIamConfig(requiredBindings, projectId, "dataconnect");
26
+ throw new error_1.FirebaseError("Unable to make required IAM policy changes.");
27
+ }
28
+ }
29
+ }
30
+ exports.grantRolesToCloudSqlServiceAccount = grantRolesToCloudSqlServiceAccount;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.upsertConnector = exports.listConnectors = exports.deleteConnector = exports.getConnector = exports.upsertSchema = exports.getSchema = exports.deleteService = exports.createService = exports.listServices = exports.listAllServices = exports.listLocations = void 0;
4
+ const api_1 = require("../api");
5
+ const apiv2_1 = require("../apiv2");
6
+ const operationPoller = require("../operation-poller");
7
+ const types = require("./types");
8
+ const logger_1 = require("../logger");
9
+ const DATACONNECT_API_VERSION = "v1alpha";
10
+ const dataconnectClient = () => new apiv2_1.Client({
11
+ urlPrefix: (0, api_1.dataconnectOrigin)(),
12
+ apiVersion: DATACONNECT_API_VERSION,
13
+ auth: true,
14
+ });
15
+ async function listLocations(projectId) {
16
+ var _a, _b, _c;
17
+ const res = await dataconnectClient().get(`/projects/${projectId}/locations`);
18
+ return (_c = (_b = (_a = res.body) === null || _a === void 0 ? void 0 : _a.locations) === null || _b === void 0 ? void 0 : _b.map((l) => l.locationId)) !== null && _c !== void 0 ? _c : [];
19
+ }
20
+ exports.listLocations = listLocations;
21
+ async function listAllServices(projectId) {
22
+ const locations = await listLocations(projectId);
23
+ let services = [];
24
+ for (const l of locations) {
25
+ try {
26
+ const locationServices = await listServices(projectId, l);
27
+ services = services.concat(locationServices);
28
+ }
29
+ catch (err) {
30
+ logger_1.logger.debug(`Unable to listServices in ${l}: ${err}`);
31
+ }
32
+ }
33
+ return services;
34
+ }
35
+ exports.listAllServices = listAllServices;
36
+ async function listServices(projectId, locationId) {
37
+ var _a;
38
+ const res = await dataconnectClient().get(`/projects/${projectId}/locations/${locationId}/services`);
39
+ return (_a = res.body.services) !== null && _a !== void 0 ? _a : [];
40
+ }
41
+ exports.listServices = listServices;
42
+ async function createService(projectId, locationId, serviceId) {
43
+ const op = await dataconnectClient().post(`/projects/${projectId}/locations/${locationId}/services`, {
44
+ name: `projects/${projectId}/locations/${locationId}/services/${serviceId}`,
45
+ }, {
46
+ queryParams: {
47
+ service_id: serviceId,
48
+ },
49
+ });
50
+ const pollRes = await operationPoller.pollOperation({
51
+ apiOrigin: (0, api_1.dataconnectOrigin)(),
52
+ apiVersion: DATACONNECT_API_VERSION,
53
+ operationResourceName: op.body.name,
54
+ });
55
+ return pollRes;
56
+ }
57
+ exports.createService = createService;
58
+ async function deleteService(projectId, locationId, serviceId) {
59
+ const op = await dataconnectClient().delete(`projects/${projectId}/locations/${locationId}/services/${serviceId}?force=true`);
60
+ const pollRes = await operationPoller.pollOperation({
61
+ apiOrigin: (0, api_1.dataconnectOrigin)(),
62
+ apiVersion: DATACONNECT_API_VERSION,
63
+ operationResourceName: op.body.name,
64
+ });
65
+ return pollRes;
66
+ }
67
+ exports.deleteService = deleteService;
68
+ async function getSchema(serviceName) {
69
+ const res = await dataconnectClient().get(`${serviceName}/schemas/${types.SCHEMA_ID}`);
70
+ return res.body;
71
+ }
72
+ exports.getSchema = getSchema;
73
+ async function upsertSchema(schema, validateOnly = false) {
74
+ const op = await dataconnectClient().patch(`${schema.name}`, schema, {
75
+ queryParams: {
76
+ allowMissing: "true",
77
+ validateOnly: validateOnly ? "true" : "false",
78
+ },
79
+ });
80
+ if (validateOnly) {
81
+ return;
82
+ }
83
+ return operationPoller.pollOperation({
84
+ apiOrigin: (0, api_1.dataconnectOrigin)(),
85
+ apiVersion: DATACONNECT_API_VERSION,
86
+ operationResourceName: op.body.name,
87
+ });
88
+ }
89
+ exports.upsertSchema = upsertSchema;
90
+ async function getConnector(name) {
91
+ const res = await dataconnectClient().get(name);
92
+ return res.body;
93
+ }
94
+ exports.getConnector = getConnector;
95
+ async function deleteConnector(name) {
96
+ const res = await dataconnectClient().delete(name);
97
+ return res.body;
98
+ }
99
+ exports.deleteConnector = deleteConnector;
100
+ async function listConnectors(serviceName) {
101
+ var _a;
102
+ const res = await dataconnectClient().get(`${serviceName}/connectors`);
103
+ return ((_a = res.body) === null || _a === void 0 ? void 0 : _a.connectors) || [];
104
+ }
105
+ exports.listConnectors = listConnectors;
106
+ async function upsertConnector(connector) {
107
+ const op = await dataconnectClient().patch(`${connector.name}?allow_missing=true`, connector);
108
+ const pollRes = await operationPoller.pollOperation({
109
+ apiOrigin: (0, api_1.dataconnectOrigin)(),
110
+ apiVersion: DATACONNECT_API_VERSION,
111
+ operationResourceName: op.body.name,
112
+ });
113
+ return pollRes;
114
+ }
115
+ exports.upsertConnector = upsertConnector;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeGraphQL = void 0;
4
+ const api_1 = require("../api");
5
+ const apiv2_1 = require("../apiv2");
6
+ const DATACONNECT_API_VERSION = "v1alpha";
7
+ const dataconnectDataplaneClient = () => new apiv2_1.Client({
8
+ urlPrefix: (0, api_1.dataconnectOrigin)(),
9
+ apiVersion: DATACONNECT_API_VERSION,
10
+ auth: true,
11
+ });
12
+ async function executeGraphQL(servicePath, body) {
13
+ const res = await dataconnectDataplaneClient().post(`${servicePath}:executeGraphql`, body, { resolveOnHTTPError: true });
14
+ return res;
15
+ }
16
+ exports.executeGraphQL = executeGraphQL;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureApis = void 0;
4
+ const api = require("../api");
5
+ const ensureApiEnabled_1 = require("../ensureApiEnabled");
6
+ async function ensureApis(projectId) {
7
+ const prefix = "dataconnect";
8
+ await (0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix);
9
+ await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix);
10
+ await (0, ensureApiEnabled_1.ensure)(projectId, api.computeOrigin(), prefix);
11
+ }
12
+ exports.ensureApis = ensureApis;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pickService = exports.readGQLFiles = exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = void 0;
4
+ const fs = require("fs-extra");
5
+ const path = require("path");
6
+ const error_1 = require("../error");
7
+ const utils_1 = require("../utils");
8
+ const load_1 = require("./load");
9
+ function readFirebaseJson(config) {
10
+ if (!config.has("dataconnect")) {
11
+ return [];
12
+ }
13
+ const validator = (cfg) => {
14
+ if (!cfg["source"] && !cfg["location"]) {
15
+ throw new error_1.FirebaseError("Invalid firebase.json: DataConnect requires `source` and `location`");
16
+ }
17
+ return {
18
+ source: cfg["source"],
19
+ location: cfg["location"],
20
+ };
21
+ };
22
+ const configs = config.get("dataconnect");
23
+ if (typeof configs === "object" && !Array.isArray(configs)) {
24
+ return [validator(configs)];
25
+ }
26
+ else if (Array.isArray(configs)) {
27
+ return configs.map(validator);
28
+ }
29
+ else {
30
+ throw new error_1.FirebaseError("Invalid firebase.json: dataconnect should be of the form { source: string, location: string }");
31
+ }
32
+ }
33
+ exports.readFirebaseJson = readFirebaseJson;
34
+ async function readDataConnectYaml(sourceDirectory) {
35
+ const file = await (0, utils_1.readFileFromDirectory)(sourceDirectory, "dataconnect.yaml");
36
+ const dataconnectYaml = await (0, utils_1.wrappedSafeLoad)(file.source);
37
+ return validateDataConnectYaml(dataconnectYaml);
38
+ }
39
+ exports.readDataConnectYaml = readDataConnectYaml;
40
+ function validateDataConnectYaml(unvalidated) {
41
+ return unvalidated;
42
+ }
43
+ async function readConnectorYaml(sourceDirectory) {
44
+ const file = await (0, utils_1.readFileFromDirectory)(sourceDirectory, "connector.yaml");
45
+ const connectorYaml = await (0, utils_1.wrappedSafeLoad)(file.source);
46
+ return validateConnectorYaml(connectorYaml);
47
+ }
48
+ exports.readConnectorYaml = readConnectorYaml;
49
+ function validateConnectorYaml(unvalidated) {
50
+ return unvalidated;
51
+ }
52
+ async function readGQLFiles(sourceDir) {
53
+ const files = await fs.readdir(sourceDir);
54
+ return files.filter((f) => f.endsWith(".gql")).map((f) => toFile(path.join(sourceDir, f)));
55
+ }
56
+ exports.readGQLFiles = readGQLFiles;
57
+ function toFile(path) {
58
+ if (!fs.existsSync(path)) {
59
+ throw new error_1.FirebaseError(`file ${path} not found`);
60
+ }
61
+ const file = fs.readFileSync(path).toString();
62
+ return {
63
+ path: path,
64
+ content: file,
65
+ };
66
+ }
67
+ async function pickService(projectId, config, serviceId) {
68
+ const serviceCfgs = readFirebaseJson(config);
69
+ let serviceInfo;
70
+ if (serviceCfgs.length === 0) {
71
+ throw new error_1.FirebaseError("No Data Connect services found in firebase.json.");
72
+ }
73
+ else if (serviceCfgs.length === 1) {
74
+ serviceInfo = await (0, load_1.load)(projectId, serviceCfgs[0].location, serviceCfgs[0].source);
75
+ }
76
+ else {
77
+ if (!serviceId) {
78
+ throw new error_1.FirebaseError("Multiple Data Connect services found in firebase.json. Please specify a service ID to use.");
79
+ }
80
+ const infos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, c.location, c.source)));
81
+ const maybe = infos.find((i) => i.dataConnectYaml.serviceId === serviceId);
82
+ if (!maybe) {
83
+ throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json.`);
84
+ }
85
+ serviceInfo = maybe;
86
+ }
87
+ return serviceInfo;
88
+ }
89
+ exports.pickService = pickService;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getResourceFilters = void 0;
4
+ const error_1 = require("../error");
5
+ function getResourceFilters(options) {
6
+ if (!options.only) {
7
+ return undefined;
8
+ }
9
+ const selectors = options.only.split(",");
10
+ const filters = [];
11
+ for (let selector of selectors) {
12
+ if (selector.startsWith("dataconnect:")) {
13
+ selector = selector.replace("dataconnect:", "");
14
+ if (selector.length > 0) {
15
+ filters.push(parseSelector(selector));
16
+ }
17
+ }
18
+ }
19
+ if (filters.length === 0) {
20
+ return undefined;
21
+ }
22
+ return filters;
23
+ }
24
+ exports.getResourceFilters = getResourceFilters;
25
+ function parseSelector(selector) {
26
+ const parts = selector.split(":");
27
+ const filter = {
28
+ serviceId: parts[0],
29
+ };
30
+ if (parts.length === 2) {
31
+ if (parts[1] === "schema") {
32
+ filter.schemaOnly = true;
33
+ }
34
+ else {
35
+ filter.connectorId = parts[1];
36
+ }
37
+ }
38
+ else if (parts.length === 1) {
39
+ filter.fullService = true;
40
+ }
41
+ else {
42
+ throw new error_1.FirebaseError(`Invalid '--only' filter dataconnect:${selector}`);
43
+ }
44
+ return filter;
45
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printFreeTrialUnavailable = exports.checkForFreeTrialInstance = exports.freeTrialTermsLink = void 0;
4
+ const cloudsqladmin_1 = require("../gcp/cloudsql/cloudsqladmin");
5
+ const utils = require("../utils");
6
+ function freeTrialTermsLink() {
7
+ return "";
8
+ }
9
+ exports.freeTrialTermsLink = freeTrialTermsLink;
10
+ async function checkForFreeTrialInstance(projectId) {
11
+ var _a;
12
+ const instances = await (0, cloudsqladmin_1.listInstances)(projectId);
13
+ return (_a = instances.find((i) => { var _a; return ((_a = i.settings.userLabels) === null || _a === void 0 ? void 0 : _a["firebase-data-connect"]) === "ft"; })) === null || _a === void 0 ? void 0 : _a.name;
14
+ }
15
+ exports.checkForFreeTrialInstance = checkForFreeTrialInstance;
16
+ function printFreeTrialUnavailable(projectId, instanceId) {
17
+ const message = `Project '${projectId}' already has a CloudSQL instance '${instanceId}' on the Firebase Data Connect free trial. ` +
18
+ "The free trial only includes one CloudSQL instance. " +
19
+ `Consider using a separate database on ${instanceId}, or creating a new CloudSQL instance at ` +
20
+ "https://console.cloud.google.com/sql/instances";
21
+ utils.logLabeledError("dataconnect", message);
22
+ }
23
+ exports.printFreeTrialUnavailable = printFreeTrialUnavailable;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prettify = void 0;
4
+ function prettify(err) {
5
+ var _a;
6
+ const message = err.message;
7
+ let header = (_a = err.extensions.file) !== null && _a !== void 0 ? _a : "";
8
+ for (const loc of err.locations) {
9
+ header += `(${loc.line}, ${loc.column})`;
10
+ }
11
+ return header.length ? `${header}: ${message}` : message;
12
+ }
13
+ exports.prettify = prettify;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.load = void 0;
4
+ const path = require("path");
5
+ const fileUtils = require("./fileUtils");
6
+ const types_1 = require("./types");
7
+ async function load(projectId, locationId, sourceDirectory) {
8
+ const dataConnectYaml = await fileUtils.readDataConnectYaml(sourceDirectory);
9
+ const serviceName = `projects/${projectId}/locations/${locationId}/services/${dataConnectYaml.serviceId}`;
10
+ const schemaDir = path.join(sourceDirectory, dataConnectYaml.schema.source);
11
+ const schemaGQLs = await fileUtils.readGQLFiles(schemaDir);
12
+ const connectorInfo = await Promise.all(dataConnectYaml.connectorDirs.map(async (dir) => {
13
+ const connectorDir = path.join(sourceDirectory, dir);
14
+ const connectorYaml = await fileUtils.readConnectorYaml(connectorDir);
15
+ const connectorGqls = await fileUtils.readGQLFiles(connectorDir);
16
+ return {
17
+ connectorYaml,
18
+ connector: {
19
+ name: `${serviceName}/connectors/${connectorYaml.connectorId}`,
20
+ source: {
21
+ files: connectorGqls,
22
+ },
23
+ },
24
+ };
25
+ }));
26
+ return {
27
+ serviceName,
28
+ sourceDirectory,
29
+ schema: {
30
+ name: `${serviceName}/schemas/${types_1.SCHEMA_ID}`,
31
+ primaryDatasource: (0, types_1.toDatasource)(projectId, locationId, dataConnectYaml.schema.datasource),
32
+ source: {
33
+ files: schemaGQLs,
34
+ },
35
+ },
36
+ dataConnectYaml,
37
+ connectorInfo,
38
+ };
39
+ }
40
+ exports.load = load;