firebase-tools 14.19.1 → 14.20.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/appUtils.js +4 -4
- package/lib/command.js +1 -0
- package/lib/commands/apps-init.js +7 -7
- package/lib/commands/dataconnect-execute.js +229 -0
- package/lib/commands/firestore-backups-delete.js +1 -6
- package/lib/commands/firestore-backups-get.js +2 -8
- package/lib/commands/firestore-backups-list.js +4 -10
- package/lib/commands/firestore-backups-schedules-create.js +1 -6
- package/lib/commands/firestore-backups-schedules-delete.js +1 -6
- package/lib/commands/firestore-backups-schedules-list.js +1 -7
- package/lib/commands/firestore-backups-schedules-update.js +1 -6
- package/lib/commands/firestore-bulkdelete.js +7 -13
- package/lib/commands/firestore-databases-create.js +5 -10
- package/lib/commands/firestore-databases-delete.js +1 -6
- package/lib/commands/firestore-databases-get.js +1 -7
- package/lib/commands/firestore-databases-list.js +1 -7
- package/lib/commands/firestore-databases-restore.js +5 -10
- package/lib/commands/firestore-databases-update.js +1 -6
- package/lib/commands/firestore-locations.js +1 -7
- package/lib/commands/firestore-operations-cancel.js +3 -9
- package/lib/commands/firestore-operations-describe.js +2 -8
- package/lib/commands/firestore-operations-list.js +2 -8
- package/lib/commands/index.js +1 -0
- package/lib/dataconnect/build.js +16 -2
- package/lib/dataconnect/load.js +21 -1
- package/lib/dataconnect/names.js +6 -1
- package/lib/dataconnect/provisionCloudSql.js +38 -11
- package/lib/dataconnect/schemaMigration.js +16 -3
- package/lib/dataconnect/types.js +1 -10
- package/lib/deploy/dataconnect/context.js +26 -0
- package/lib/deploy/dataconnect/deploy.js +13 -4
- package/lib/deploy/dataconnect/prepare.js +11 -8
- package/lib/deploy/dataconnect/release.js +10 -2
- package/lib/deploy/index.js +39 -20
- package/lib/emulator/downloadableEmulatorInfo.json +18 -18
- package/lib/gcp/cloudsql/cloudsqladmin.js +4 -3
- package/lib/init/features/dataconnect/index.js +22 -6
- package/lib/init/features/dataconnect/sdk.js +40 -22
- package/lib/management/apps.js +24 -24
- package/lib/mcp/prompts/core/consult.js +2 -3
- package/lib/mcp/prompts/core/init.js +3 -4
- package/lib/mcp/resources/index.js +0 -4
- package/lib/mcp/tools/dataconnect/execute.js +0 -1
- package/lib/mcp/util/dataconnect/converter.js +5 -4
- package/lib/mcp/util/dataconnect/emulator.js +0 -1
- package/lib/responseToError.js +7 -6
- package/package.json +1 -1
- package/lib/dataconnect/appFinder.js +0 -103
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.command = void 0;
|
|
4
4
|
const command_1 = require("../command");
|
|
5
5
|
const fsi = require("../firestore/api");
|
|
6
|
-
const logger_1 = require("../logger");
|
|
7
6
|
const types_1 = require("../emulator/types");
|
|
8
7
|
const commandUtils_1 = require("../emulator/commandUtils");
|
|
9
8
|
const pretty_print_1 = require("../firestore/pretty-print");
|
|
@@ -18,12 +17,7 @@ exports.command = new command_1.Command("firestore:operations:list")
|
|
|
18
17
|
const limit = options.limit === undefined ? 100 : Number(options.limit);
|
|
19
18
|
const api = new fsi.FirestoreApi();
|
|
20
19
|
const { operations } = await api.listOperations(options.project, databaseId, limit);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
const printer = new pretty_print_1.PrettyPrint();
|
|
26
|
-
printer.prettyPrintOperations(operations);
|
|
27
|
-
}
|
|
20
|
+
const printer = new pretty_print_1.PrettyPrint();
|
|
21
|
+
printer.prettyPrintOperations(operations);
|
|
28
22
|
return operations;
|
|
29
23
|
});
|
package/lib/commands/index.js
CHANGED
|
@@ -226,6 +226,7 @@ function load(client) {
|
|
|
226
226
|
client.setup.emulators.ui = loadCommand("setup-emulators-ui");
|
|
227
227
|
client.dataconnect = {};
|
|
228
228
|
client.setup.emulators.dataconnect = loadCommand("setup-emulators-dataconnect");
|
|
229
|
+
client.dataconnect.execute = loadCommand("dataconnect-execute");
|
|
229
230
|
client.dataconnect.services = {};
|
|
230
231
|
client.dataconnect.services.list = loadCommand("dataconnect-services-list");
|
|
231
232
|
client.dataconnect.sql = {};
|
package/lib/dataconnect/build.js
CHANGED
|
@@ -7,7 +7,7 @@ const prompt_1 = require("../prompt");
|
|
|
7
7
|
const utils = require("../utils");
|
|
8
8
|
const graphqlError_1 = require("./graphqlError");
|
|
9
9
|
const auth_1 = require("../auth");
|
|
10
|
-
async function build(options, configDir,
|
|
10
|
+
async function build(options, configDir, deployStats) {
|
|
11
11
|
var _a, _b;
|
|
12
12
|
const account = (0, auth_1.getProjectDefaultAccount)(options.projectRoot);
|
|
13
13
|
const args = { configDir, account };
|
|
@@ -16,7 +16,21 @@ async function build(options, configDir, dryRun) {
|
|
|
16
16
|
}
|
|
17
17
|
const buildResult = await dataconnectEmulator_1.DataConnectEmulator.build(args);
|
|
18
18
|
if ((_a = buildResult === null || buildResult === void 0 ? void 0 : buildResult.errors) === null || _a === void 0 ? void 0 : _a.length) {
|
|
19
|
-
|
|
19
|
+
buildResult.errors.forEach((e) => {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
if ((_a = e.extensions) === null || _a === void 0 ? void 0 : _a.warningLevel) {
|
|
22
|
+
let key = e.extensions.warningLevel.toLowerCase();
|
|
23
|
+
const msgSp = e.message.split(": ");
|
|
24
|
+
if (msgSp.length >= 2) {
|
|
25
|
+
key += `_${msgSp[0].toLowerCase()}`;
|
|
26
|
+
}
|
|
27
|
+
deployStats.numBuildWarnings.set(key, ((_b = deployStats.numBuildWarnings.get(key)) !== null && _b !== void 0 ? _b : 0) + 1);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
deployStats.numBuildErrors += 1;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
await handleBuildErrors(buildResult.errors, options.nonInteractive, options.force, options.dryRun);
|
|
20
34
|
}
|
|
21
35
|
return (_b = buildResult === null || buildResult === void 0 ? void 0 : buildResult.metadata) !== null && _b !== void 0 ? _b : {};
|
|
22
36
|
}
|
package/lib/dataconnect/load.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = exports.load = exports.loadAll = exports.pickService = void 0;
|
|
3
|
+
exports.squashGraphQL = exports.readGQLFiles = exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = exports.load = exports.loadAll = exports.pickService = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const fs = require("fs-extra");
|
|
6
6
|
const clc = require("colorette");
|
|
@@ -130,6 +130,7 @@ async function readGQLFiles(sourceDir) {
|
|
|
130
130
|
const files = await (0, glob_1.glob)("**/*.{gql,graphql}", { cwd: sourceDir, absolute: true, nodir: true });
|
|
131
131
|
return files.map((f) => toFile(sourceDir, f));
|
|
132
132
|
}
|
|
133
|
+
exports.readGQLFiles = readGQLFiles;
|
|
133
134
|
function toFile(sourceDir, fullPath) {
|
|
134
135
|
const relPath = path.relative(sourceDir, fullPath);
|
|
135
136
|
if (!fs.existsSync(fullPath)) {
|
|
@@ -141,3 +142,22 @@ function toFile(sourceDir, fullPath) {
|
|
|
141
142
|
content,
|
|
142
143
|
};
|
|
143
144
|
}
|
|
145
|
+
function squashGraphQL(source) {
|
|
146
|
+
if (!source.files || !source.files.length) {
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
if (source.files.length === 1) {
|
|
150
|
+
return source.files[0].content;
|
|
151
|
+
}
|
|
152
|
+
let query = "";
|
|
153
|
+
for (const f of source.files) {
|
|
154
|
+
if (!f.content || !/\S/.test(f.content)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
query += `### Begin file ${f.path}\n`;
|
|
158
|
+
query += f.content;
|
|
159
|
+
query += `### End file ${f.path}\n`;
|
|
160
|
+
}
|
|
161
|
+
return query;
|
|
162
|
+
}
|
|
163
|
+
exports.squashGraphQL = squashGraphQL;
|
package/lib/dataconnect/names.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseCloudSQLInstanceName = exports.parseConnectorName = exports.parseServiceName = void 0;
|
|
3
|
+
exports.isGraphqlName = 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) {
|
|
@@ -67,3 +67,8 @@ function parseCloudSQLInstanceName(cloudSQLInstanceName) {
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
exports.parseCloudSQLInstanceName = parseCloudSQLInstanceName;
|
|
70
|
+
const graphqlNameRegex = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
71
|
+
function isGraphqlName(name) {
|
|
72
|
+
return graphqlNameRegex.test(name);
|
|
73
|
+
}
|
|
74
|
+
exports.isGraphqlName = isGraphqlName;
|
|
@@ -1,27 +1,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getUpdateReason = exports.cloudSQLBeingCreated = exports.setupCloudSql = void 0;
|
|
4
|
-
const cloudSqlAdminClient = require("../gcp/cloudsql/cloudsqladmin");
|
|
5
|
-
const utils = require("../utils");
|
|
6
4
|
const clc = require("colorette");
|
|
7
|
-
const
|
|
8
|
-
const utils_1 = require("../utils");
|
|
5
|
+
const cloudSqlAdminClient = require("../gcp/cloudsql/cloudsqladmin");
|
|
9
6
|
const logger_1 = require("../logger");
|
|
7
|
+
const checkIam_1 = require("./checkIam");
|
|
10
8
|
const freeTrial_1 = require("./freeTrial");
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
const track_1 = require("../track");
|
|
11
|
+
const utils = require("../utils");
|
|
11
12
|
const GOOGLE_ML_INTEGRATION_ROLE = "roles/aiplatform.user";
|
|
12
13
|
async function setupCloudSql(args) {
|
|
13
|
-
|
|
14
|
+
var _a;
|
|
14
15
|
const { projectId, instanceId, requireGoogleMlIntegration, dryRun } = args;
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
const stats = { action: "get" };
|
|
18
|
+
let success = false;
|
|
19
|
+
try {
|
|
20
|
+
await upsertInstance(stats, Object.assign({}, args));
|
|
21
|
+
success = true;
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
if (!dryRun) {
|
|
25
|
+
await (0, track_1.trackGA4)("dataconnect_cloud_sql", {
|
|
26
|
+
source: args.source,
|
|
27
|
+
action: success ? stats.action : `${stats.action}_error`,
|
|
28
|
+
location: args.location,
|
|
29
|
+
enable_google_ml_integration: args.requireGoogleMlIntegration.toString(),
|
|
30
|
+
database_version: ((_a = stats.databaseVersion) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || "unknown",
|
|
31
|
+
dataconnect_label: stats.dataconnectLabel || "unknown",
|
|
32
|
+
}, Date.now() - startTime);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
15
35
|
if (requireGoogleMlIntegration && !dryRun) {
|
|
16
36
|
await (0, checkIam_1.grantRolesToCloudSqlServiceAccount)(projectId, instanceId, [GOOGLE_ML_INTEGRATION_ROLE]);
|
|
17
37
|
}
|
|
18
38
|
}
|
|
19
39
|
exports.setupCloudSql = setupCloudSql;
|
|
20
|
-
async function upsertInstance(args) {
|
|
40
|
+
async function upsertInstance(stats, args) {
|
|
41
|
+
var _a, _b;
|
|
21
42
|
const { projectId, instanceId, requireGoogleMlIntegration, dryRun } = args;
|
|
22
43
|
try {
|
|
23
44
|
const existingInstance = await cloudSqlAdminClient.getInstance(projectId, instanceId);
|
|
24
45
|
utils.logLabeledBullet("dataconnect", `Found existing Cloud SQL instance ${clc.bold(instanceId)}.`);
|
|
46
|
+
stats.databaseVersion = existingInstance.databaseVersion;
|
|
47
|
+
stats.dataconnectLabel = (_b = (_a = existingInstance.settings) === null || _a === void 0 ? void 0 : _a.userLabels) === null || _b === void 0 ? void 0 : _b["firebase-data-connect"];
|
|
25
48
|
const why = getUpdateReason(existingInstance, requireGoogleMlIntegration);
|
|
26
49
|
if (why) {
|
|
27
50
|
if (dryRun) {
|
|
@@ -32,6 +55,7 @@ async function upsertInstance(args) {
|
|
|
32
55
|
else {
|
|
33
56
|
utils.logLabeledBullet("dataconnect", `Cloud SQL instance ${clc.bold(instanceId)} settings are not compatible with Firebase Data Connect. ` +
|
|
34
57
|
why);
|
|
58
|
+
stats.action = "update";
|
|
35
59
|
await (0, utils_1.promiseWithSpinner)(() => cloudSqlAdminClient.updateInstanceForDataConnect(existingInstance, requireGoogleMlIntegration), "Updating your Cloud SQL instance...");
|
|
36
60
|
}
|
|
37
61
|
}
|
|
@@ -41,12 +65,15 @@ async function upsertInstance(args) {
|
|
|
41
65
|
if (err.status !== 404) {
|
|
42
66
|
throw err;
|
|
43
67
|
}
|
|
44
|
-
|
|
68
|
+
stats.action = "create";
|
|
69
|
+
stats.databaseVersion = cloudSqlAdminClient.DEFAULT_DATABASE_VERSION;
|
|
70
|
+
const freeTrialUsed = await (0, freeTrial_1.checkFreeTrialInstanceUsed)(projectId);
|
|
71
|
+
stats.dataconnectLabel = freeTrialUsed ? "nt" : "ft";
|
|
72
|
+
await createInstance(Object.assign(Object.assign({}, args), { freeTrialLabel: stats.dataconnectLabel }));
|
|
45
73
|
}
|
|
46
74
|
}
|
|
47
75
|
async function createInstance(args) {
|
|
48
|
-
const { projectId, location, instanceId, requireGoogleMlIntegration, dryRun } = args;
|
|
49
|
-
const freeTrialUsed = await (0, freeTrial_1.checkFreeTrialInstanceUsed)(projectId);
|
|
76
|
+
const { projectId, location, instanceId, requireGoogleMlIntegration, dryRun, freeTrialLabel } = args;
|
|
50
77
|
if (dryRun) {
|
|
51
78
|
utils.logLabeledBullet("dataconnect", `Cloud SQL Instance ${clc.bold(instanceId)} not found. It will be created on your next deploy.`);
|
|
52
79
|
}
|
|
@@ -56,9 +83,9 @@ async function createInstance(args) {
|
|
|
56
83
|
location,
|
|
57
84
|
instanceId,
|
|
58
85
|
enableGoogleMlIntegration: requireGoogleMlIntegration,
|
|
59
|
-
|
|
86
|
+
freeTrialLabel,
|
|
60
87
|
});
|
|
61
|
-
utils.logLabeledBullet("dataconnect", cloudSQLBeingCreated(projectId, instanceId,
|
|
88
|
+
utils.logLabeledBullet("dataconnect", cloudSQLBeingCreated(projectId, instanceId, freeTrialLabel === "ft"));
|
|
62
89
|
}
|
|
63
90
|
}
|
|
64
91
|
function cloudSQLBeingCreated(projectId, instanceId, includeFreeTrialToS) {
|
|
@@ -104,7 +104,7 @@ async function diffSchema(options, schema, schemaValidation) {
|
|
|
104
104
|
exports.diffSchema = diffSchema;
|
|
105
105
|
async function migrateSchema(args) {
|
|
106
106
|
var _a;
|
|
107
|
-
const { options, schema, validateOnly, schemaValidation } = args;
|
|
107
|
+
const { options, schema, validateOnly, schemaValidation, stats } = args;
|
|
108
108
|
let validationMode = schemaValidation !== null && schemaValidation !== void 0 ? schemaValidation : "COMPATIBLE";
|
|
109
109
|
setSchemaValidationMode(schema, validationMode);
|
|
110
110
|
displayStartSchemaDiff(validationMode);
|
|
@@ -113,6 +113,9 @@ async function migrateSchema(args) {
|
|
|
113
113
|
await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, true);
|
|
114
114
|
const existingInstance = await cloudSqlAdminClient.getInstance(projectId, instanceId);
|
|
115
115
|
if (existingInstance.state === "PENDING_CREATE") {
|
|
116
|
+
if (stats) {
|
|
117
|
+
stats.numSchemaSkippedDueToPendingCreate++;
|
|
118
|
+
}
|
|
116
119
|
const postgresql = (_a = schema.datasources.find((d) => d.postgresql)) === null || _a === void 0 ? void 0 : _a.postgresql;
|
|
117
120
|
if (!postgresql) {
|
|
118
121
|
throw new error_1.FirebaseError(`Cannot find Postgres datasource in the schema to deploy: ${serviceName}/schemas/${types_1.SCHEMA_ID}.\nIts datasources: ${JSON.stringify(schema.datasources)}`);
|
|
@@ -145,6 +148,14 @@ async function migrateSchema(args) {
|
|
|
145
148
|
}
|
|
146
149
|
throw err;
|
|
147
150
|
}
|
|
151
|
+
if (stats) {
|
|
152
|
+
if (incompatible) {
|
|
153
|
+
stats.numSchemaSqlDiffs += incompatible.diffs.length;
|
|
154
|
+
}
|
|
155
|
+
if (invalidConnectors.length) {
|
|
156
|
+
stats.numSchemaInvalidConnectors += invalidConnectors.length;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
148
159
|
const migrationMode = await promptForSchemaMigration(options, instanceId, databaseId, incompatible, validateOnly, validationMode);
|
|
149
160
|
const shouldDeleteInvalidConnectors = await promptForInvalidConnectorError(options, serviceName, invalidConnectors, validateOnly);
|
|
150
161
|
if (incompatible) {
|
|
@@ -174,10 +185,12 @@ async function migrateSchema(args) {
|
|
|
174
185
|
throw err;
|
|
175
186
|
}
|
|
176
187
|
const incompatible = errors.getIncompatibleSchemaError(err);
|
|
177
|
-
|
|
178
|
-
if (!incompatible && !invalidConnectors.length) {
|
|
188
|
+
if (!incompatible) {
|
|
179
189
|
throw err;
|
|
180
190
|
}
|
|
191
|
+
if (stats && incompatible) {
|
|
192
|
+
stats.numSchemaSqlDiffs += incompatible.diffs.length;
|
|
193
|
+
}
|
|
181
194
|
const migrationMode = await promptForSchemaMigration(options, instanceId, databaseId, incompatible, validateOnly, "STRICT_AFTER_COMPATIBLE");
|
|
182
195
|
if (incompatible) {
|
|
183
196
|
const maybeDiffs = await handleIncompatibleSchemaError({
|
package/lib/dataconnect/types.js
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isGraphQLResponseError = exports.isGraphQLResponse = exports.toDatasource = exports.
|
|
3
|
+
exports.isGraphQLResponseError = exports.isGraphQLResponse = exports.toDatasource = exports.requiresVector = exports.SCHEMA_ID = void 0;
|
|
4
4
|
exports.SCHEMA_ID = "main";
|
|
5
5
|
function requiresVector(dm) {
|
|
6
6
|
var _a, _b, _c, _d;
|
|
7
7
|
return (_d = (_c = (_b = (_a = dm === null || dm === void 0 ? void 0 : dm.primaryDataSource) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.requiredExtensions) === null || _c === void 0 ? void 0 : _c.includes("vector")) !== null && _d !== void 0 ? _d : false;
|
|
8
8
|
}
|
|
9
9
|
exports.requiresVector = requiresVector;
|
|
10
|
-
var Platform;
|
|
11
|
-
(function (Platform) {
|
|
12
|
-
Platform["NONE"] = "NONE";
|
|
13
|
-
Platform["ANDROID"] = "ANDROID";
|
|
14
|
-
Platform["WEB"] = "WEB";
|
|
15
|
-
Platform["IOS"] = "IOS";
|
|
16
|
-
Platform["FLUTTER"] = "FLUTTER";
|
|
17
|
-
Platform["MULTIPLE"] = "MULTIPLE";
|
|
18
|
-
})(Platform = exports.Platform || (exports.Platform = {}));
|
|
19
10
|
function toDatasource(projectId, locationId, ds) {
|
|
20
11
|
if (ds === null || ds === void 0 ? void 0 : ds.postgresql) {
|
|
21
12
|
return {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deployStatsParams = exports.initDeployStats = void 0;
|
|
4
|
+
function initDeployStats() {
|
|
5
|
+
return {
|
|
6
|
+
numBuildErrors: 0,
|
|
7
|
+
numBuildWarnings: new Map(),
|
|
8
|
+
numServiceCreated: 0,
|
|
9
|
+
numServiceDeleted: 0,
|
|
10
|
+
numSchemaMigrated: 0,
|
|
11
|
+
numConnectorUpdatedBeforeSchema: 0,
|
|
12
|
+
numConnectorUpdatedAfterSchema: 0,
|
|
13
|
+
numSchemaSkippedDueToPendingCreate: 0,
|
|
14
|
+
numSchemaSqlDiffs: 0,
|
|
15
|
+
numSchemaInvalidConnectors: 0,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
exports.initDeployStats = initDeployStats;
|
|
19
|
+
function deployStatsParams(stats) {
|
|
20
|
+
const buildWarnings = {};
|
|
21
|
+
for (const [type, num] of stats.numBuildWarnings.entries()) {
|
|
22
|
+
buildWarnings[`num_build_warnings_${type}`] = num;
|
|
23
|
+
}
|
|
24
|
+
return Object.assign({ missing_billing: (!!stats.missingBilling).toString(), num_service_created: stats.numServiceCreated, num_service_deleted: stats.numServiceDeleted, num_schema_migrated: stats.numSchemaMigrated, num_connector_updated_before_schema: stats.numConnectorUpdatedBeforeSchema, num_connector_updated_after_schema: stats.numConnectorUpdatedAfterSchema, num_schema_skipped_due_to_pending_create: stats.numSchemaSkippedDueToPendingCreate, num_schema_sql_diffs: stats.numSchemaSqlDiffs, num_schema_invalid_connectors: stats.numSchemaInvalidConnectors, num_build_errors: stats.numBuildErrors }, buildWarnings);
|
|
25
|
+
}
|
|
26
|
+
exports.deployStatsParams = deployStatsParams;
|
|
@@ -10,10 +10,14 @@ const api_1 = require("../../api");
|
|
|
10
10
|
const ensureApiEnabled = require("../../ensureApiEnabled");
|
|
11
11
|
const prompt_1 = require("../../prompt");
|
|
12
12
|
async function default_1(context, options) {
|
|
13
|
+
const dataconnect = context.dataconnect;
|
|
14
|
+
if (!dataconnect) {
|
|
15
|
+
throw new Error("dataconnect.prepare must be run before dataconnect.deploy");
|
|
16
|
+
}
|
|
13
17
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
14
|
-
const serviceInfos =
|
|
18
|
+
const serviceInfos = dataconnect.serviceInfos;
|
|
15
19
|
const services = await client.listAllServices(projectId);
|
|
16
|
-
const filters =
|
|
20
|
+
const filters = dataconnect.filters;
|
|
17
21
|
if (serviceInfos.some((si) => {
|
|
18
22
|
return (0, types_1.requiresVector)(si.deploymentMetadata);
|
|
19
23
|
})) {
|
|
@@ -22,11 +26,14 @@ async function default_1(context, options) {
|
|
|
22
26
|
const servicesToCreate = serviceInfos
|
|
23
27
|
.filter((si) => !services.some((s) => matches(si, s)))
|
|
24
28
|
.filter((si) => {
|
|
25
|
-
return !filters ||
|
|
29
|
+
return (!filters ||
|
|
30
|
+
(filters === null || filters === void 0 ? void 0 : filters.some((f) => si.dataConnectYaml.serviceId === f.serviceId)));
|
|
26
31
|
});
|
|
32
|
+
dataconnect.deployStats.numServiceCreated = servicesToCreate.length;
|
|
27
33
|
const servicesToDelete = filters
|
|
28
34
|
? []
|
|
29
35
|
: services.filter((s) => !serviceInfos.some((si) => matches(si, s)));
|
|
36
|
+
dataconnect.deployStats.numServiceDeleted = servicesToDelete.length;
|
|
30
37
|
await Promise.all(servicesToCreate.map(async (s) => {
|
|
31
38
|
const { projectId, locationId, serviceId } = splitName(s.serviceName);
|
|
32
39
|
await client.createService(projectId, locationId, serviceId);
|
|
@@ -49,7 +56,8 @@ async function default_1(context, options) {
|
|
|
49
56
|
utils.logLabeledBullet("dataconnect", "Checking for CloudSQL resources...");
|
|
50
57
|
await Promise.all(serviceInfos
|
|
51
58
|
.filter((si) => {
|
|
52
|
-
return !filters ||
|
|
59
|
+
return (!filters ||
|
|
60
|
+
(filters === null || filters === void 0 ? void 0 : filters.some((f) => si.dataConnectYaml.serviceId === f.serviceId)));
|
|
53
61
|
})
|
|
54
62
|
.map(async (s) => {
|
|
55
63
|
var _a, _b, _c;
|
|
@@ -66,6 +74,7 @@ async function default_1(context, options) {
|
|
|
66
74
|
instanceId,
|
|
67
75
|
databaseId,
|
|
68
76
|
requireGoogleMlIntegration: (0, types_1.requiresVector)(s.deploymentMetadata),
|
|
77
|
+
source: "deploy",
|
|
69
78
|
});
|
|
70
79
|
}
|
|
71
80
|
}));
|
|
@@ -17,18 +17,24 @@ const error_1 = require("../../error");
|
|
|
17
17
|
const types_1 = require("../../dataconnect/types");
|
|
18
18
|
const schemaMigration_1 = require("../../dataconnect/schemaMigration");
|
|
19
19
|
const freeTrial_1 = require("../../dataconnect/freeTrial");
|
|
20
|
+
const context_1 = require("./context");
|
|
20
21
|
async function default_1(context, options) {
|
|
21
22
|
var _a, _b, _c;
|
|
22
23
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
24
|
+
await (0, ensureApis_1.ensureApis)(projectId);
|
|
25
|
+
context.dataconnect = {
|
|
26
|
+
serviceInfos: await (0, load_1.loadAll)(projectId, options.config),
|
|
27
|
+
filters: (0, filters_1.getResourceFilters)(options),
|
|
28
|
+
deployStats: (0, context_1.initDeployStats)(),
|
|
29
|
+
};
|
|
30
|
+
const { serviceInfos, filters, deployStats } = context.dataconnect;
|
|
23
31
|
if (!(await (0, cloudbilling_1.checkBillingEnabled)(projectId))) {
|
|
32
|
+
deployStats.missingBilling = true;
|
|
24
33
|
throw new error_1.FirebaseError((0, freeTrial_1.upgradeInstructions)(projectId));
|
|
25
34
|
}
|
|
26
|
-
await (0, ensureApis_1.ensureApis)(projectId);
|
|
27
35
|
await (0, requireTosAcceptance_1.requireTosAcceptance)(firedata_1.DATA_CONNECT_TOS_ID)(options);
|
|
28
|
-
const filters = (0, filters_1.getResourceFilters)(options);
|
|
29
|
-
const serviceInfos = await (0, load_1.loadAll)(projectId, options.config);
|
|
30
36
|
for (const si of serviceInfos) {
|
|
31
|
-
si.deploymentMetadata = await (0, build_1.build)(options, si.sourceDirectory,
|
|
37
|
+
si.deploymentMetadata = await (0, build_1.build)(options, si.sourceDirectory, deployStats);
|
|
32
38
|
}
|
|
33
39
|
const unmatchedFilters = filters === null || filters === void 0 ? void 0 : filters.filter((f) => {
|
|
34
40
|
const serviceMatched = serviceInfos.some((s) => s.dataConnectYaml.serviceId === f.serviceId);
|
|
@@ -43,10 +49,6 @@ async function default_1(context, options) {
|
|
|
43
49
|
if (unmatchedFilters === null || unmatchedFilters === void 0 ? void 0 : unmatchedFilters.length) {
|
|
44
50
|
throw new error_1.FirebaseError(`The following filters were specified in --only but didn't match anything in this project: ${unmatchedFilters.map(filters_1.toString).map(clc.bold).join(", ")}`);
|
|
45
51
|
}
|
|
46
|
-
context.dataconnect = {
|
|
47
|
-
serviceInfos,
|
|
48
|
-
filters,
|
|
49
|
-
};
|
|
50
52
|
utils.logLabeledBullet("dataconnect", `Successfully compiled schema and connectors`);
|
|
51
53
|
if (options.dryRun) {
|
|
52
54
|
for (const si of serviceInfos) {
|
|
@@ -73,6 +75,7 @@ async function default_1(context, options) {
|
|
|
73
75
|
databaseId,
|
|
74
76
|
requireGoogleMlIntegration: (0, types_1.requiresVector)(s.deploymentMetadata),
|
|
75
77
|
dryRun: true,
|
|
78
|
+
source: "deploy",
|
|
76
79
|
});
|
|
77
80
|
}
|
|
78
81
|
}));
|
|
@@ -8,9 +8,13 @@ const projectUtils_1 = require("../../projectUtils");
|
|
|
8
8
|
const names_1 = require("../../dataconnect/names");
|
|
9
9
|
const logger_1 = require("../../logger");
|
|
10
10
|
async function default_1(context, options) {
|
|
11
|
+
const dataconnect = context.dataconnect;
|
|
12
|
+
if (!dataconnect) {
|
|
13
|
+
throw new Error("dataconnect.prepare must be run before dataconnect.release");
|
|
14
|
+
}
|
|
11
15
|
const project = (0, projectUtils_1.needProjectId)(options);
|
|
12
|
-
const serviceInfos =
|
|
13
|
-
const filters =
|
|
16
|
+
const serviceInfos = dataconnect.serviceInfos;
|
|
17
|
+
const filters = dataconnect.filters;
|
|
14
18
|
const wantSchemas = serviceInfos
|
|
15
19
|
.filter((si) => {
|
|
16
20
|
return (!filters ||
|
|
@@ -43,6 +47,7 @@ async function default_1(context, options) {
|
|
|
43
47
|
return c;
|
|
44
48
|
}
|
|
45
49
|
utils.logLabeledSuccess("dataconnect", `Deployed connector ${c.name}`);
|
|
50
|
+
dataconnect.deployStats.numConnectorUpdatedBeforeSchema++;
|
|
46
51
|
return undefined;
|
|
47
52
|
}));
|
|
48
53
|
for (const s of wantSchemas) {
|
|
@@ -51,13 +56,16 @@ async function default_1(context, options) {
|
|
|
51
56
|
schema: s.schema,
|
|
52
57
|
validateOnly: false,
|
|
53
58
|
schemaValidation: s.validationMode,
|
|
59
|
+
stats: dataconnect.deployStats,
|
|
54
60
|
});
|
|
55
61
|
utils.logLabeledSuccess("dataconnect", `Migrated schema ${s.schema.name}`);
|
|
62
|
+
dataconnect.deployStats.numSchemaMigrated++;
|
|
56
63
|
}
|
|
57
64
|
await Promise.all(remainingConnectors.map(async (c) => {
|
|
58
65
|
if (c) {
|
|
59
66
|
await (0, client_1.upsertConnector)(c);
|
|
60
67
|
utils.logLabeledSuccess("dataconnect", `Deployed connector ${c.name}`);
|
|
68
|
+
dataconnect.deployStats.numConnectorUpdatedAfterSchema++;
|
|
61
69
|
}
|
|
62
70
|
}));
|
|
63
71
|
const allConnectors = await deployedConnectors(serviceInfos);
|
package/lib/deploy/index.js
CHANGED
|
@@ -26,6 +26,7 @@ const prepare_1 = require("./hosting/prepare");
|
|
|
26
26
|
const github_1 = require("../init/features/hosting/github");
|
|
27
27
|
const deploy_1 = require("../commands/deploy");
|
|
28
28
|
const requirePermissions_1 = require("../requirePermissions");
|
|
29
|
+
const context_1 = require("./dataconnect/context");
|
|
29
30
|
const TARGETS = {
|
|
30
31
|
hosting: HostingTarget,
|
|
31
32
|
database: DatabaseTarget,
|
|
@@ -63,7 +64,7 @@ const isDeployingWebFramework = (options) => {
|
|
|
63
64
|
};
|
|
64
65
|
exports.isDeployingWebFramework = isDeployingWebFramework;
|
|
65
66
|
const deploy = async function (targetNames, options, customContext = {}) {
|
|
66
|
-
var _a, _b, _c;
|
|
67
|
+
var _a, _b, _c, _d;
|
|
67
68
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
68
69
|
const payload = {};
|
|
69
70
|
const context = Object.assign({ projectId }, customContext);
|
|
@@ -115,34 +116,52 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
115
116
|
logger_1.logger.info((0, colorette_1.bold)((0, colorette_1.white)("===") + " Deploying to '" + projectId + "'..."));
|
|
116
117
|
logger_1.logger.info();
|
|
117
118
|
(0, utils_1.logBullet)("deploying " + (0, colorette_1.bold)(targetNames.join(", ")));
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
let result = "predeploys_error";
|
|
120
|
+
try {
|
|
121
|
+
await chain(predeploys, context, options, payload);
|
|
122
|
+
result = "prepares_error";
|
|
123
|
+
await chain(prepares, context, options, payload);
|
|
124
|
+
result = "deploys_error";
|
|
125
|
+
await chain(deploys, context, options, payload);
|
|
126
|
+
result = "releases_error";
|
|
127
|
+
await chain(releases, context, options, payload);
|
|
128
|
+
result = "postdeploys_error";
|
|
129
|
+
await chain(postdeploys, context, options, payload);
|
|
130
|
+
result = "success";
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
const baseParams = {
|
|
134
|
+
interactive: options.nonInteractive ? "false" : "true",
|
|
135
|
+
dry_run: options.dryRun ? "true" : "false",
|
|
136
|
+
result: result,
|
|
137
|
+
};
|
|
138
|
+
const duration = Date.now() - startTime;
|
|
139
|
+
const params = Object.assign({}, baseParams);
|
|
140
|
+
Object.keys(TARGETS).reduce((accum, t) => {
|
|
141
|
+
accum[t] = "false";
|
|
142
|
+
return accum;
|
|
143
|
+
}, params);
|
|
144
|
+
for (const t of targetNames) {
|
|
145
|
+
params[t] = "true";
|
|
146
|
+
}
|
|
147
|
+
void (0, track_1.trackGA4)("product_deploy", params, duration);
|
|
148
|
+
const stats = (_a = context === null || context === void 0 ? void 0 : context.dataconnect) === null || _a === void 0 ? void 0 : _a.deployStats;
|
|
149
|
+
if (stats) {
|
|
150
|
+
const fdcParams = (0, context_1.deployStatsParams)(stats);
|
|
151
|
+
void (0, track_1.trackGA4)("dataconnect_deploy", Object.assign(Object.assign({}, fdcParams), baseParams), duration);
|
|
152
|
+
}
|
|
133
153
|
}
|
|
134
|
-
await (0, track_1.trackGA4)("product_deploy", analyticsParams, duration);
|
|
135
154
|
const successMessage = options.dryRun ? "Dry run complete!" : "Deploy complete!";
|
|
136
155
|
logger_1.logger.info();
|
|
137
156
|
(0, utils_1.logSuccess)((0, colorette_1.bold)((0, colorette_1.underline)(successMessage)));
|
|
138
157
|
logger_1.logger.info();
|
|
139
158
|
const deployedHosting = (0, lodash_1.includes)(targetNames, "hosting");
|
|
140
|
-
logger_1.logger.info((0, colorette_1.bold)("Project Console:"), (0, utils_1.consoleUrl)((
|
|
159
|
+
logger_1.logger.info((0, colorette_1.bold)("Project Console:"), (0, utils_1.consoleUrl)((_b = options.project) !== null && _b !== void 0 ? _b : "_", "/overview"));
|
|
141
160
|
if (deployedHosting) {
|
|
142
|
-
(0, lodash_1.each)((
|
|
161
|
+
(0, lodash_1.each)((_c = context.hosting) === null || _c === void 0 ? void 0 : _c.deploys, (deploy) => {
|
|
143
162
|
logger_1.logger.info((0, colorette_1.bold)("Hosting URL:"), (0, utils_1.addSubdomain)((0, api_1.hostingOrigin)(), deploy.config.site));
|
|
144
163
|
});
|
|
145
|
-
const versionNames = (
|
|
164
|
+
const versionNames = (_d = context.hosting) === null || _d === void 0 ? void 0 : _d.deploys.map((deploy) => deploy.version);
|
|
146
165
|
return { hosting: (versionNames === null || versionNames === void 0 ? void 0 : versionNames.length) === 1 ? versionNames[0] : versionNames };
|
|
147
166
|
}
|
|
148
167
|
else {
|
|
@@ -54,28 +54,28 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "2.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
57
|
+
"version": "2.15.0",
|
|
58
|
+
"expectedSize": 29610848,
|
|
59
|
+
"expectedChecksum": "182ddca17f4974ec081fd5a769f8eeac",
|
|
60
|
+
"expectedChecksumSHA256": "3bcdc39b7f149f8c2d96f397188ca003daf1b43a1ce845780146b79217970a52",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.15.0",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.0"
|
|
63
63
|
},
|
|
64
64
|
"win32": {
|
|
65
|
-
"version": "2.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
65
|
+
"version": "2.15.0",
|
|
66
|
+
"expectedSize": 30099456,
|
|
67
|
+
"expectedChecksum": "5dd4efd368c3987925c41784e21c7af6",
|
|
68
|
+
"expectedChecksumSHA256": "a664f29cd21f826dd4936f8b95f0673e0ea603ab0d85a426d8231f1aaa8947f9",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.15.0",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.0.exe"
|
|
71
71
|
},
|
|
72
72
|
"linux": {
|
|
73
|
-
"version": "2.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
73
|
+
"version": "2.15.0",
|
|
74
|
+
"expectedSize": 29532344,
|
|
75
|
+
"expectedChecksum": "38baf423fbfc9c67a89975a27f0a3974",
|
|
76
|
+
"expectedChecksumSHA256": "d3c5b26470639fedce9e3ac667948fa07473e77f6c2d0b361d78c5f2e856a1ec",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.15.0",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.0"
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.listUsers = exports.deleteUser = exports.getUser = exports.createUser = exports.deleteDatabase = exports.createDatabase = exports.getDatabase = exports.listDatabases = exports.updateInstanceForDataConnect = exports.createInstance = exports.instanceConsoleLink = exports.getInstance = exports.listInstances = exports.iamUserIsCSQLAdmin = void 0;
|
|
3
|
+
exports.listUsers = exports.deleteUser = exports.getUser = exports.createUser = exports.deleteDatabase = exports.createDatabase = exports.getDatabase = exports.listDatabases = exports.updateInstanceForDataConnect = exports.createInstance = exports.DEFAULT_DATABASE_VERSION = exports.instanceConsoleLink = exports.getInstance = exports.listInstances = exports.iamUserIsCSQLAdmin = void 0;
|
|
4
4
|
const apiv2_1 = require("../../apiv2");
|
|
5
5
|
const api_1 = require("../../api");
|
|
6
6
|
const clc = require("colorette");
|
|
@@ -51,6 +51,7 @@ function instanceConsoleLink(projectId, instanceId) {
|
|
|
51
51
|
return `https://console.cloud.google.com/sql/instances/${instanceId}/overview?project=${projectId}`;
|
|
52
52
|
}
|
|
53
53
|
exports.instanceConsoleLink = instanceConsoleLink;
|
|
54
|
+
exports.DEFAULT_DATABASE_VERSION = "POSTGRES_15";
|
|
54
55
|
async function createInstance(args) {
|
|
55
56
|
const databaseFlags = [{ name: "cloudsql.iam_authentication", value: "on" }];
|
|
56
57
|
if (args.enableGoogleMlIntegration) {
|
|
@@ -60,7 +61,7 @@ async function createInstance(args) {
|
|
|
60
61
|
await client.post(`projects/${args.projectId}/instances`, {
|
|
61
62
|
name: args.instanceId,
|
|
62
63
|
region: args.location,
|
|
63
|
-
databaseVersion:
|
|
64
|
+
databaseVersion: exports.DEFAULT_DATABASE_VERSION,
|
|
64
65
|
settings: {
|
|
65
66
|
tier: "db-f1-micro",
|
|
66
67
|
edition: "ENTERPRISE",
|
|
@@ -70,7 +71,7 @@ async function createInstance(args) {
|
|
|
70
71
|
enableGoogleMlIntegration: args.enableGoogleMlIntegration,
|
|
71
72
|
databaseFlags,
|
|
72
73
|
storageAutoResize: false,
|
|
73
|
-
userLabels: { "firebase-data-connect": args.
|
|
74
|
+
userLabels: { "firebase-data-connect": args.freeTrialLabel },
|
|
74
75
|
insightsConfig: {
|
|
75
76
|
queryInsightsEnabled: true,
|
|
76
77
|
queryPlansPerMinute: 5,
|