firebase-tools 13.11.0 → 13.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/dataconnect-sdk-generate.js +11 -0
- package/lib/dataconnect/client.js +3 -19
- package/lib/dataconnect/names.js +22 -1
- package/lib/dataconnect/provisionCloudSql.js +13 -7
- package/lib/deploy/dataconnect/deploy.js +1 -0
- package/lib/emulator/controller.js +1 -1
- package/lib/emulator/dataconnectEmulator.js +14 -3
- package/lib/gcp/cloudsql/cloudsqladmin.js +13 -2
- package/lib/init/features/dataconnect/index.js +28 -18
- package/package.json +1 -1
|
@@ -20,6 +20,17 @@ exports.command = new command_1.Command("dataconnect:sdk:generate")
|
|
|
20
20
|
configDir = path.resolve(path.join(cwd), configDir);
|
|
21
21
|
}
|
|
22
22
|
const serviceInfo = await (0, load_1.load)(projectId, service.location, configDir);
|
|
23
|
+
const hasGeneratables = serviceInfo.connectorInfo.some((c) => {
|
|
24
|
+
var _a, _b, _c;
|
|
25
|
+
return (((_a = c.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) ||
|
|
26
|
+
((_b = c.connectorYaml.generate) === null || _b === void 0 ? void 0 : _b.kotlinSdk) ||
|
|
27
|
+
((_c = c.connectorYaml.generate) === null || _c === void 0 ? void 0 : _c.swiftSdk));
|
|
28
|
+
});
|
|
29
|
+
if (!hasGeneratables) {
|
|
30
|
+
logger_1.logger.warn("No generated SDKs have been declared in connector.yaml files.");
|
|
31
|
+
logger_1.logger.warn("See https://firebase.google.com/docs/data-connect/quickstart#configure-sdk-outputs for examples of how to configure generated SDKs.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
23
34
|
for (const conn of serviceInfo.connectorInfo) {
|
|
24
35
|
const output = await dataconnectEmulator_1.DataConnectEmulator.generate({
|
|
25
36
|
configDir,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
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.
|
|
3
|
+
exports.upsertConnector = exports.listConnectors = exports.deleteConnector = exports.getConnector = exports.upsertSchema = exports.getSchema = exports.deleteService = exports.createService = exports.listAllServices = exports.listLocations = void 0;
|
|
4
4
|
const api_1 = require("../api");
|
|
5
5
|
const apiv2_1 = require("../apiv2");
|
|
6
6
|
const operationPoller = require("../operation-poller");
|
|
7
7
|
const types = require("./types");
|
|
8
|
-
const logger_1 = require("../logger");
|
|
9
8
|
const DATACONNECT_API_VERSION = "v1alpha";
|
|
10
9
|
const dataconnectClient = () => new apiv2_1.Client({
|
|
11
10
|
urlPrefix: (0, api_1.dataconnectOrigin)(),
|
|
@@ -19,26 +18,11 @@ async function listLocations(projectId) {
|
|
|
19
18
|
}
|
|
20
19
|
exports.listLocations = listLocations;
|
|
21
20
|
async function listAllServices(projectId) {
|
|
22
|
-
const locations = await listLocations(projectId);
|
|
23
|
-
let services = [];
|
|
24
|
-
await Promise.all(locations.map(async (l) => {
|
|
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
21
|
var _a;
|
|
38
|
-
const res = await dataconnectClient().get(`/projects/${projectId}/locations
|
|
22
|
+
const res = await dataconnectClient().get(`/projects/${projectId}/locations/-/services`);
|
|
39
23
|
return (_a = res.body.services) !== null && _a !== void 0 ? _a : [];
|
|
40
24
|
}
|
|
41
|
-
exports.
|
|
25
|
+
exports.listAllServices = listAllServices;
|
|
42
26
|
async function createService(projectId, locationId, serviceId) {
|
|
43
27
|
const op = await dataconnectClient().post(`/projects/${projectId}/locations/${locationId}/services`, {
|
|
44
28
|
name: `projects/${projectId}/locations/${locationId}/services/${serviceId}`,
|
package/lib/dataconnect/names.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseConnectorName = exports.parseServiceName = void 0;
|
|
3
|
+
exports.parseCloudSQLInstanceName = exports.parseConnectorName = exports.parseServiceName = void 0;
|
|
4
4
|
const error_1 = require("../error");
|
|
5
5
|
const serviceNameRegex = /projects\/(?<projectId>[^\/]+)\/locations\/(?<location>[^\/]+)\/services\/(?<serviceId>[^\/]+)/;
|
|
6
6
|
function parseServiceName(serviceName) {
|
|
@@ -46,3 +46,24 @@ function parseConnectorName(connectorName) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
exports.parseConnectorName = parseConnectorName;
|
|
49
|
+
const cloudSQLInstanceNameRegex = /projects\/(?<projectId>[^\/]+)\/locations\/(?<location>[^\/]+)\/instances\/(?<instanceId>[^\/]+)/;
|
|
50
|
+
function parseCloudSQLInstanceName(cloudSQLInstanceName) {
|
|
51
|
+
var _a, _b, _c;
|
|
52
|
+
const res = cloudSQLInstanceNameRegex.exec(cloudSQLInstanceName);
|
|
53
|
+
const projectId = (_a = res === null || res === void 0 ? void 0 : res.groups) === null || _a === void 0 ? void 0 : _a.projectId;
|
|
54
|
+
const location = (_b = res === null || res === void 0 ? void 0 : res.groups) === null || _b === void 0 ? void 0 : _b.location;
|
|
55
|
+
const instanceId = (_c = res === null || res === void 0 ? void 0 : res.groups) === null || _c === void 0 ? void 0 : _c.instanceId;
|
|
56
|
+
if (!projectId || !location || !instanceId) {
|
|
57
|
+
throw new error_1.FirebaseError(`${cloudSQLInstanceName} is not a valid cloudSQL instance name`);
|
|
58
|
+
}
|
|
59
|
+
const toString = () => {
|
|
60
|
+
return `projects/${projectId}/locations/${location}/services/${instanceId}`;
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
projectId,
|
|
64
|
+
location,
|
|
65
|
+
instanceId,
|
|
66
|
+
toString,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
exports.parseCloudSQLInstanceName = parseCloudSQLInstanceName;
|
|
@@ -9,8 +9,8 @@ const GOOGLE_ML_INTEGRATION_ROLE = "roles/aiplatform.user";
|
|
|
9
9
|
const freeTrial_1 = require("./freeTrial");
|
|
10
10
|
const error_1 = require("../error");
|
|
11
11
|
async function provisionCloudSql(args) {
|
|
12
|
-
let connectionName;
|
|
13
|
-
const { projectId, locationId, instanceId, databaseId, enableGoogleMlIntegration, silent } = args;
|
|
12
|
+
let connectionName = "";
|
|
13
|
+
const { projectId, locationId, instanceId, databaseId, enableGoogleMlIntegration, waitForCreation, silent, } = args;
|
|
14
14
|
try {
|
|
15
15
|
const existingInstance = await cloudSqlAdminClient.getInstance(projectId, instanceId);
|
|
16
16
|
silent || utils.logLabeledBullet("dataconnect", `Found existing instance ${instanceId}.`);
|
|
@@ -35,11 +35,17 @@ async function provisionCloudSql(args) {
|
|
|
35
35
|
throw new error_1.FirebaseError("Free trial unavailable.");
|
|
36
36
|
}
|
|
37
37
|
silent ||
|
|
38
|
-
utils.logLabeledBullet("dataconnect", `CloudSQL instance '${instanceId}' not found, creating it
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
utils.logLabeledBullet("dataconnect", `CloudSQL instance '${instanceId}' not found, creating it.` +
|
|
39
|
+
`\nThis instance is provided under the terms of the Data Connect free trial ${(0, freeTrial_1.freeTrialTermsLink)()}` +
|
|
40
|
+
`\nMonitor the progress at ${cloudSqlAdminClient.instanceConsoleLink(projectId, instanceId)}`);
|
|
41
|
+
const newInstance = await (0, utils_1.promiseWithSpinner)(() => cloudSqlAdminClient.createInstance(projectId, locationId, instanceId, enableGoogleMlIntegration, waitForCreation), "Creating your instance...");
|
|
42
|
+
if (newInstance) {
|
|
43
|
+
silent || utils.logLabeledBullet("dataconnect", "Instance created");
|
|
44
|
+
connectionName = (newInstance === null || newInstance === void 0 ? void 0 : newInstance.connectionName) || "";
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
silent || utils.logLabeledBullet("dataconnect", "Instance creation process started");
|
|
48
|
+
}
|
|
43
49
|
}
|
|
44
50
|
try {
|
|
45
51
|
await cloudSqlAdminClient.getDatabase(projectId, instanceId, databaseId);
|
|
@@ -540,7 +540,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
540
540
|
});
|
|
541
541
|
await startEmulator(dataConnectEmulator);
|
|
542
542
|
if (!utils.isVSCodeExtension()) {
|
|
543
|
-
dataConnectEmulator.connectToPostgres();
|
|
543
|
+
await dataConnectEmulator.connectToPostgres();
|
|
544
544
|
}
|
|
545
545
|
}
|
|
546
546
|
if (listenForEmulator.storage) {
|
|
@@ -118,11 +118,22 @@ class DataConnectEmulator {
|
|
|
118
118
|
exports.DataConnectEmulator = DataConnectEmulator;
|
|
119
119
|
class DataConnectEmulatorClient {
|
|
120
120
|
constructor() {
|
|
121
|
-
this.client =
|
|
121
|
+
this.client = undefined;
|
|
122
122
|
}
|
|
123
123
|
async configureEmulator(body) {
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
if (!this.client) {
|
|
125
|
+
this.client = registry_1.EmulatorRegistry.client(types_1.Emulators.DATACONNECT);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const res = await this.client.post("emulator/configure", body);
|
|
129
|
+
return res;
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
if (err.status === 500) {
|
|
133
|
+
throw new error_1.FirebaseError(`Data Connect emulator: ${err.context.body.message}`);
|
|
134
|
+
}
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
126
137
|
}
|
|
127
138
|
}
|
|
128
139
|
exports.DataConnectEmulatorClient = DataConnectEmulatorClient;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
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;
|
|
3
|
+
exports.listUsers = exports.deleteUser = exports.getUser = exports.createUser = exports.createDatabase = exports.getDatabase = exports.listDatabases = exports.updateInstanceForDataConnect = exports.createInstance = exports.instanceConsoleLink = exports.getInstance = exports.listInstances = void 0;
|
|
4
4
|
const apiv2_1 = require("../../apiv2");
|
|
5
5
|
const api_1 = require("../../api");
|
|
6
6
|
const operationPoller = require("../../operation-poller");
|
|
7
|
+
const error_1 = require("../../error");
|
|
7
8
|
const API_VERSION = "v1";
|
|
8
9
|
const client = new apiv2_1.Client({
|
|
9
10
|
urlPrefix: (0, api_1.cloudSQLAdminOrigin)(),
|
|
@@ -18,10 +19,17 @@ async function listInstances(projectId) {
|
|
|
18
19
|
exports.listInstances = listInstances;
|
|
19
20
|
async function getInstance(projectId, instanceId) {
|
|
20
21
|
const res = await client.get(`projects/${projectId}/instances/${instanceId}`);
|
|
22
|
+
if (res.body.state === "FAILED") {
|
|
23
|
+
throw new error_1.FirebaseError(`Cloud SQL instance ${instanceId} is in a failed state.\nGo to ${instanceConsoleLink(projectId, instanceId)} to repair or delete it.`);
|
|
24
|
+
}
|
|
21
25
|
return res.body;
|
|
22
26
|
}
|
|
23
27
|
exports.getInstance = getInstance;
|
|
24
|
-
|
|
28
|
+
function instanceConsoleLink(projectId, instanceId) {
|
|
29
|
+
return `https://console.cloud.google.com/sql/instances/${instanceId}/overview?project=${projectId}`;
|
|
30
|
+
}
|
|
31
|
+
exports.instanceConsoleLink = instanceConsoleLink;
|
|
32
|
+
async function createInstance(projectId, location, instanceId, enableGoogleMlIntegration, waitForCreation) {
|
|
25
33
|
const databaseFlags = [{ name: "cloudsql.iam_authentication", value: "on" }];
|
|
26
34
|
if (enableGoogleMlIntegration) {
|
|
27
35
|
databaseFlags.push({ name: "cloudsql.enable_google_ml_integration", value: "on" });
|
|
@@ -47,6 +55,9 @@ async function createInstance(projectId, location, instanceId, enableGoogleMlInt
|
|
|
47
55
|
},
|
|
48
56
|
},
|
|
49
57
|
});
|
|
58
|
+
if (!waitForCreation) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
50
61
|
const opName = `projects/${projectId}/operations/${op.body.name}`;
|
|
51
62
|
const pollRes = await operationPoller.pollOperation({
|
|
52
63
|
apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
|
|
@@ -11,6 +11,7 @@ const ensureApis_1 = require("../../../dataconnect/ensureApis");
|
|
|
11
11
|
const client_1 = require("../../../dataconnect/client");
|
|
12
12
|
const emulators_1 = require("../emulators");
|
|
13
13
|
const names_1 = require("../../../dataconnect/names");
|
|
14
|
+
const logger_1 = require("../../../logger");
|
|
14
15
|
const TEMPLATE_ROOT = (0, path_1.resolve)(__dirname, "../../../../templates/init/dataconnect/");
|
|
15
16
|
const DATACONNECT_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "dataconnect.yaml"), "utf8");
|
|
16
17
|
const CONNECTOR_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "connector.yaml"), "utf8");
|
|
@@ -46,6 +47,10 @@ async function doSetup(setup, config) {
|
|
|
46
47
|
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
47
48
|
const subbedDataconnectYaml = subValues(DATACONNECT_YAML_TEMPLATE, info);
|
|
48
49
|
const subbedConnectorYaml = subValues(CONNECTOR_YAML_TEMPLATE, info);
|
|
50
|
+
if (!config.has("dataconnect")) {
|
|
51
|
+
config.set("dataconnect.source", dir);
|
|
52
|
+
config.set("dataconnect.location", info.locationId);
|
|
53
|
+
}
|
|
49
54
|
await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
|
|
50
55
|
await config.askWriteProjectFile((0, path_1.join)(dir, "schema", "schema.gql"), SCHEMA_TEMPLATE);
|
|
51
56
|
await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "connector.yaml"), subbedConnectorYaml);
|
|
@@ -63,6 +68,7 @@ async function doSetup(setup, config) {
|
|
|
63
68
|
instanceId: info.cloudSqlInstanceId,
|
|
64
69
|
databaseId: info.cloudSqlDatabase,
|
|
65
70
|
enableGoogleMlIntegration: false,
|
|
71
|
+
waitForCreation: false,
|
|
66
72
|
});
|
|
67
73
|
}
|
|
68
74
|
}
|
|
@@ -114,8 +120,10 @@ async function promptForService(setup, info) {
|
|
|
114
120
|
info.serviceId = serviceName.serviceId;
|
|
115
121
|
info.locationId = serviceName.location;
|
|
116
122
|
if (choice.schema) {
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
if ((_a = choice.schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) {
|
|
124
|
+
const instanceName = (0, names_1.parseCloudSQLInstanceName)((_b = choice.schema.primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.cloudSql.instance);
|
|
125
|
+
info.cloudSqlInstanceId = instanceName.instanceId;
|
|
126
|
+
}
|
|
119
127
|
info.cloudSqlDatabase = (_d = (_c = choice.schema.primaryDatasource.postgresql) === null || _c === void 0 ? void 0 : _c.database) !== null && _d !== void 0 ? _d : "";
|
|
120
128
|
}
|
|
121
129
|
}
|
|
@@ -147,7 +155,9 @@ async function promptForCloudSQLInstance(setup, info) {
|
|
|
147
155
|
type: "list",
|
|
148
156
|
choices,
|
|
149
157
|
});
|
|
150
|
-
info.
|
|
158
|
+
if (info.cloudSqlInstanceId !== "") {
|
|
159
|
+
info.locationId = choices.find((c) => c.value === info.cloudSqlInstanceId).location;
|
|
160
|
+
}
|
|
151
161
|
}
|
|
152
162
|
}
|
|
153
163
|
if (info.cloudSqlInstanceId === "") {
|
|
@@ -189,23 +199,23 @@ async function locationChoices(setup) {
|
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
async function promptForDatabase(setup, config, info) {
|
|
192
|
-
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
193
|
-
if (!config.has("dataconnect")) {
|
|
194
|
-
config.set("dataconnect.source", dir);
|
|
195
|
-
config.set("dataconnect.location", info.locationId);
|
|
196
|
-
}
|
|
197
202
|
if (!info.isNewInstance && setup.projectId) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
choices.push({ name: "Create a new database", value: "" });
|
|
203
|
-
if (dbs.length) {
|
|
204
|
-
info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
205
|
-
message: `Which database in ${info.cloudSqlInstanceId} would you like to use?`,
|
|
206
|
-
type: "list",
|
|
207
|
-
choices,
|
|
203
|
+
try {
|
|
204
|
+
const dbs = await cloudsql.listDatabases(setup.projectId, info.cloudSqlInstanceId);
|
|
205
|
+
const choices = dbs.map((d) => {
|
|
206
|
+
return { name: d.name, value: d.name };
|
|
208
207
|
});
|
|
208
|
+
choices.push({ name: "Create a new database", value: "" });
|
|
209
|
+
if (dbs.length) {
|
|
210
|
+
info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
211
|
+
message: `Which database in ${info.cloudSqlInstanceId} would you like to use?`,
|
|
212
|
+
type: "list",
|
|
213
|
+
choices,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
logger_1.logger.debug(`[dataconnect] Cannot list databases during init: ${err}`);
|
|
209
219
|
}
|
|
210
220
|
}
|
|
211
221
|
if (info.cloudSqlDatabase === "") {
|