firebase-tools 13.10.2 → 13.11.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.
- package/lib/commands/dataconnect-services-list.js +6 -12
- package/lib/crashlytics/buildToolsJarHelper.js +2 -1
- package/lib/dataconnect/client.js +13 -21
- package/lib/dataconnect/graphqlError.js +7 -4
- package/lib/dataconnect/provisionCloudSql.js +13 -7
- package/lib/dataconnect/schemaMigration.js +2 -8
- package/lib/deploy/dataconnect/deploy.js +1 -0
- package/lib/deploy/functions/prepare.js +1 -1
- package/lib/deploy/functions/runtimes/supported/types.js +6 -0
- package/lib/emulator/controller.js +3 -0
- package/lib/emulator/dataconnectEmulator.js +56 -11
- package/lib/emulator/download.js +4 -0
- package/lib/emulator/downloadableEmulators.js +32 -19
- package/lib/emulator/storage/rules/runtime.js +1 -1
- package/lib/frameworks/angular/utils.js +8 -3
- package/lib/gcp/cloudsql/cloudsqladmin.js +13 -2
- package/lib/init/features/dataconnect/index.js +170 -109
- package/lib/rc.js +1 -3
- package/lib/track.js +29 -2
- package/package.json +1 -1
- package/schema/firebase-config.json +2 -0
- package/templates/init/dataconnect/dataconnect.yaml +1 -1
- package/templates/init/dataconnect/mutations.gql +1 -1
- package/templates/init/dataconnect/queries.gql +6 -6
|
@@ -17,7 +17,7 @@ exports.command = new command_1.Command("dataconnect:services:list")
|
|
|
17
17
|
"dataconnect.connectors.list",
|
|
18
18
|
])
|
|
19
19
|
.action(async (options) => {
|
|
20
|
-
var _a, _b, _c, _d, _e, _f;
|
|
20
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
21
21
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
22
22
|
await (0, ensureApis_1.ensureApis)(projectId);
|
|
23
23
|
const services = await client.listAllServices(projectId);
|
|
@@ -34,28 +34,22 @@ exports.command = new command_1.Command("dataconnect:services:list")
|
|
|
34
34
|
});
|
|
35
35
|
const jsonOutput = { services: [] };
|
|
36
36
|
for (const service of services) {
|
|
37
|
-
|
|
37
|
+
const schema = (_a = (await client.getSchema(service.name))) !== null && _a !== void 0 ? _a : {
|
|
38
38
|
name: "",
|
|
39
39
|
primaryDatasource: {},
|
|
40
40
|
source: { files: [] },
|
|
41
41
|
};
|
|
42
|
-
try {
|
|
43
|
-
schema = await client.getSchema(service.name);
|
|
44
|
-
}
|
|
45
|
-
catch (err) {
|
|
46
|
-
logger_1.logger.debug(`Error fetching schema: ${err}`);
|
|
47
|
-
}
|
|
48
42
|
const connectors = await client.listConnectors(service.name);
|
|
49
43
|
const serviceName = names.parseServiceName(service.name);
|
|
50
|
-
const instanceName = (
|
|
44
|
+
const instanceName = (_c = (_b = schema === null || schema === void 0 ? void 0 : schema.primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.cloudSql.instance) !== null && _c !== void 0 ? _c : "";
|
|
51
45
|
const instanceId = instanceName.split("/").pop();
|
|
52
|
-
const dbId = (
|
|
46
|
+
const dbId = (_e = (_d = schema === null || schema === void 0 ? void 0 : schema.primaryDatasource.postgresql) === null || _d === void 0 ? void 0 : _d.database) !== null && _e !== void 0 ? _e : "";
|
|
53
47
|
const dbName = `CloudSQL Instance: ${instanceId}\nDatabase:${dbId}`;
|
|
54
48
|
table.push([
|
|
55
49
|
serviceName.serviceId,
|
|
56
50
|
serviceName.location,
|
|
57
51
|
dbName,
|
|
58
|
-
(
|
|
52
|
+
(_f = schema === null || schema === void 0 ? void 0 : schema.updateTime) !== null && _f !== void 0 ? _f : "",
|
|
59
53
|
"",
|
|
60
54
|
"",
|
|
61
55
|
]);
|
|
@@ -71,7 +65,7 @@ exports.command = new command_1.Command("dataconnect:services:list")
|
|
|
71
65
|
table.push(["", "", "", "", connectorName.connectorId, conn.updateTime]);
|
|
72
66
|
serviceJson.connectors.push({
|
|
73
67
|
connectorId: connectorName.connectorId,
|
|
74
|
-
connectorLastUpdated: (
|
|
68
|
+
connectorLastUpdated: (_g = conn.updateTime) !== null && _g !== void 0 ? _g : "",
|
|
75
69
|
});
|
|
76
70
|
}
|
|
77
71
|
jsonOutput.services.push(serviceJson);
|
|
@@ -12,10 +12,11 @@ const rimraf = require("rimraf");
|
|
|
12
12
|
const utils = require("../utils");
|
|
13
13
|
const JAR_CACHE_DIR = process.env.FIREBASE_CRASHLYTICS_BUILDTOOLS_PATH ||
|
|
14
14
|
path.join(os.homedir(), ".cache", "firebase", "crashlytics", "buildtools");
|
|
15
|
-
const JAR_VERSION = "
|
|
15
|
+
const JAR_VERSION = "3.0.0";
|
|
16
16
|
const JAR_URL = `https://dl.google.com/android/maven2/com/google/firebase/firebase-crashlytics-buildtools/${JAR_VERSION}/firebase-crashlytics-buildtools-${JAR_VERSION}.jar`;
|
|
17
17
|
async function fetchBuildtoolsJar() {
|
|
18
18
|
if (process.env.CRASHLYTICS_LOCAL_JAR) {
|
|
19
|
+
logger_1.logger.debug(`Using local Crashlytics Jar override at ${process.env.CRASHLYTICS_LOCAL_JAR}`);
|
|
19
20
|
return process.env.CRASHLYTICS_LOCAL_JAR;
|
|
20
21
|
}
|
|
21
22
|
const jarPath = path.join(JAR_CACHE_DIR, `crashlytics-buildtools-${JAR_VERSION}.jar`);
|
|
@@ -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}`,
|
|
@@ -66,8 +50,16 @@ async function deleteService(projectId, locationId, serviceId) {
|
|
|
66
50
|
}
|
|
67
51
|
exports.deleteService = deleteService;
|
|
68
52
|
async function getSchema(serviceName) {
|
|
69
|
-
|
|
70
|
-
|
|
53
|
+
try {
|
|
54
|
+
const res = await dataconnectClient().get(`${serviceName}/schemas/${types.SCHEMA_ID}`);
|
|
55
|
+
return res.body;
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
if (err.status !== 404) {
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
71
63
|
}
|
|
72
64
|
exports.getSchema = getSchema;
|
|
73
65
|
async function upsertSchema(schema, validateOnly = false) {
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.prettify = void 0;
|
|
4
4
|
function prettify(err) {
|
|
5
|
-
var _a;
|
|
5
|
+
var _a, _b, _c, _d;
|
|
6
6
|
const message = err.message;
|
|
7
|
-
let header = (_a = err.extensions.file) !== null &&
|
|
8
|
-
if (err.locations) {
|
|
9
|
-
|
|
7
|
+
let header = (_b = (_a = err.extensions) === null || _a === void 0 ? void 0 : _a.file) !== null && _b !== void 0 ? _b : "";
|
|
8
|
+
if (err.locations && err.locations.length) {
|
|
9
|
+
const line = (_d = (_c = err.locations[0]) === null || _c === void 0 ? void 0 : _c.line) !== null && _d !== void 0 ? _d : "";
|
|
10
|
+
if (line) {
|
|
11
|
+
header += `:${line}`;
|
|
12
|
+
}
|
|
10
13
|
}
|
|
11
14
|
return header.length ? `${header}: ${message}` : message;
|
|
12
15
|
}
|
|
@@ -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);
|
|
@@ -205,14 +205,8 @@ function displayInvalidConnectors(invalidConnectors) {
|
|
|
205
205
|
(0, utils_1.logLabeledWarning)("dataconnect", `This is a ${clc.red("breaking")} change and may break existing apps.`);
|
|
206
206
|
}
|
|
207
207
|
async function ensureServiceIsConnectedToCloudSql(serviceName, instanceId, databaseId, linkIfNotConnected) {
|
|
208
|
-
let currentSchema;
|
|
209
|
-
|
|
210
|
-
currentSchema = await (0, client_1.getSchema)(serviceName);
|
|
211
|
-
}
|
|
212
|
-
catch (err) {
|
|
213
|
-
if (err.status !== 404) {
|
|
214
|
-
throw err;
|
|
215
|
-
}
|
|
208
|
+
let currentSchema = await (0, client_1.getSchema)(serviceName);
|
|
209
|
+
if (!currentSchema) {
|
|
216
210
|
if (!linkIfNotConnected) {
|
|
217
211
|
(0, utils_1.logLabeledWarning)("dataconnect", `Not yet linked to the Cloud SQL instance.`);
|
|
218
212
|
return;
|
|
@@ -290,7 +290,7 @@ async function loadCodebases(config, options, firebaseConfig, runtimeConfig, fil
|
|
|
290
290
|
const firebaseJsonRuntime = codebaseConfig.runtime;
|
|
291
291
|
if (firebaseJsonRuntime && !supported.isRuntime(firebaseJsonRuntime)) {
|
|
292
292
|
throw new error_1.FirebaseError(`Functions codebase ${codebase} has invalid runtime ` +
|
|
293
|
-
`${firebaseJsonRuntime} specified in firebase.json. Valid values are: ` +
|
|
293
|
+
`${firebaseJsonRuntime} specified in firebase.json. Valid values are: \n` +
|
|
294
294
|
Object.keys(supported.RUNTIMES)
|
|
295
295
|
.map((s) => `- ${s}`)
|
|
296
296
|
.join("\n"));
|
|
@@ -53,6 +53,12 @@ exports.RUNTIMES = runtimes({
|
|
|
53
53
|
deprecationDate: "2026-04-30",
|
|
54
54
|
decommissionDate: "2026-10-31",
|
|
55
55
|
},
|
|
56
|
+
nodejs22: {
|
|
57
|
+
friendly: "Node.js 22",
|
|
58
|
+
status: "beta",
|
|
59
|
+
deprecationDate: "2027-04-30",
|
|
60
|
+
decommissionDate: "2027-10-31",
|
|
61
|
+
},
|
|
56
62
|
python310: {
|
|
57
63
|
friendly: "Python 3.10",
|
|
58
64
|
status: "GA",
|
|
@@ -539,6 +539,9 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
539
539
|
rc: options.rc,
|
|
540
540
|
});
|
|
541
541
|
await startEmulator(dataConnectEmulator);
|
|
542
|
+
if (!utils.isVSCodeExtension()) {
|
|
543
|
+
await dataConnectEmulator.connectToPostgres();
|
|
544
|
+
}
|
|
542
545
|
}
|
|
543
546
|
if (listenForEmulator.storage) {
|
|
544
547
|
const storageAddr = legacyGetFirstAddr(types_1.Emulators.STORAGE);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DataConnectEmulator = void 0;
|
|
3
|
+
exports.DataConnectEmulatorClient = exports.DataConnectEmulator = void 0;
|
|
4
4
|
const childProcess = require("child_process");
|
|
5
5
|
const api_1 = require("../api");
|
|
6
6
|
const constants_1 = require("./constants");
|
|
@@ -10,23 +10,35 @@ const error_1 = require("../error");
|
|
|
10
10
|
const emulatorLogger_1 = require("./emulatorLogger");
|
|
11
11
|
const types_2 = require("../dataconnect/types");
|
|
12
12
|
const portUtils_1 = require("./portUtils");
|
|
13
|
+
const registry_1 = require("./registry");
|
|
13
14
|
class DataConnectEmulator {
|
|
14
15
|
constructor(args) {
|
|
15
16
|
this.args = args;
|
|
16
17
|
this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT);
|
|
18
|
+
this.emulatorClient = new DataConnectEmulatorClient();
|
|
17
19
|
}
|
|
18
20
|
async start() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
try {
|
|
22
|
+
const info = await DataConnectEmulator.build({ configDir: this.args.configDir });
|
|
23
|
+
if ((0, types_2.requiresVector)(info.metadata)) {
|
|
24
|
+
if (constants_1.Constants.isDemoProject(this.args.projectId)) {
|
|
25
|
+
this.logger.logLabeled("WARN", "Data Connect", "Detected a 'demo-' project, but vector embeddings require a real project. Operations that use vector_embed will fail.");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.logger.logLabeled("WARN", "Data Connect", "Operations that use vector_embed will make calls to production Vertex AI");
|
|
29
|
+
}
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
|
-
|
|
32
|
+
catch (err) {
|
|
33
|
+
this.logger.log("DEBUG", `'fdc build' failed with error: ${err.message}`);
|
|
34
|
+
}
|
|
35
|
+
return (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, {
|
|
36
|
+
auto_download: this.args.auto_download,
|
|
37
|
+
listen: (0, portUtils_1.listenSpecsToString)(this.args.listen),
|
|
38
|
+
config_dir: this.args.configDir,
|
|
39
|
+
project_id: this.args.projectId,
|
|
40
|
+
service_location: this.args.locationId,
|
|
41
|
+
});
|
|
30
42
|
}
|
|
31
43
|
connect() {
|
|
32
44
|
return Promise.resolve();
|
|
@@ -73,8 +85,11 @@ class DataConnectEmulator {
|
|
|
73
85
|
original: res.error,
|
|
74
86
|
});
|
|
75
87
|
}
|
|
88
|
+
if (res.status !== 0) {
|
|
89
|
+
throw new error_1.FirebaseError(`Unable to build your Data Connect schema and connectors (exit code ${res.status}): ${res.stderr}`);
|
|
90
|
+
}
|
|
76
91
|
if (res.stderr) {
|
|
77
|
-
|
|
92
|
+
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT).log("DEBUG", res.stderr);
|
|
78
93
|
}
|
|
79
94
|
try {
|
|
80
95
|
return JSON.parse(res.stdout);
|
|
@@ -90,5 +105,35 @@ class DataConnectEmulator {
|
|
|
90
105
|
}
|
|
91
106
|
return (_b = (_a = this.args.rc.getDataconnect()) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.localConnectionString;
|
|
92
107
|
}
|
|
108
|
+
async connectToPostgres(localConnectionString, database, serviceId) {
|
|
109
|
+
const connectionString = localConnectionString !== null && localConnectionString !== void 0 ? localConnectionString : this.getLocalConectionString();
|
|
110
|
+
if (!connectionString) {
|
|
111
|
+
this.logger.log("DEBUG", "No Postgres connection string found, not connecting to Postgres");
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
await this.emulatorClient.configureEmulator({ connectionString, database, serviceId });
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
93
117
|
}
|
|
94
118
|
exports.DataConnectEmulator = DataConnectEmulator;
|
|
119
|
+
class DataConnectEmulatorClient {
|
|
120
|
+
constructor() {
|
|
121
|
+
this.client = undefined;
|
|
122
|
+
}
|
|
123
|
+
async configureEmulator(body) {
|
|
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
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.DataConnectEmulatorClient = DataConnectEmulatorClient;
|
package/lib/emulator/download.js
CHANGED
|
@@ -13,6 +13,10 @@ const downloadUtils = require("../downloadUtils");
|
|
|
13
13
|
tmp.setGracefulCleanup();
|
|
14
14
|
async function downloadEmulator(name) {
|
|
15
15
|
const emulator = downloadableEmulators.getDownloadDetails(name);
|
|
16
|
+
if (emulator.localOnly) {
|
|
17
|
+
emulatorLogger_1.EmulatorLogger.forEmulator(name).logLabeled("WARN", name, `Env variable override detected, skipping download. Using ${emulator} emulator at ${emulator.binaryPath}`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
16
20
|
emulatorLogger_1.EmulatorLogger.forEmulator(name).logLabeled("BULLET", name, `downloading ${path.basename(emulator.downloadPath)}...`);
|
|
17
21
|
fs.ensureDirSync(emulator.opts.cacheDir);
|
|
18
22
|
const tmpfile = await downloadUtils.downloadToTmp(emulator.opts.remoteUrl, !!emulator.opts.auth);
|
|
@@ -23,9 +23,9 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
23
23
|
expectedChecksum: "2fd771101c0e1f7898c04c9204f2ce63",
|
|
24
24
|
},
|
|
25
25
|
firestore: {
|
|
26
|
-
version: "1.19.
|
|
27
|
-
expectedSize:
|
|
28
|
-
expectedChecksum: "
|
|
26
|
+
version: "1.19.7",
|
|
27
|
+
expectedSize: 66438992,
|
|
28
|
+
expectedChecksum: "aec233bea95c5cfab03881574ec16d6c",
|
|
29
29
|
},
|
|
30
30
|
storage: {
|
|
31
31
|
version: "1.1.3",
|
|
@@ -40,26 +40,26 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
40
40
|
expectedChecksum: "a7f4398a00e5ca22abdcd78dc3877d00",
|
|
41
41
|
},
|
|
42
42
|
pubsub: {
|
|
43
|
-
version: "0.8.
|
|
44
|
-
expectedSize:
|
|
45
|
-
expectedChecksum: "
|
|
43
|
+
version: "0.8.14",
|
|
44
|
+
expectedSize: 66786933,
|
|
45
|
+
expectedChecksum: "a9025b3e53fdeafd2969ccb3ba1e1d38",
|
|
46
46
|
},
|
|
47
47
|
dataconnect: process.platform === "darwin"
|
|
48
48
|
? {
|
|
49
|
-
version: "1.
|
|
50
|
-
expectedSize:
|
|
51
|
-
expectedChecksum: "
|
|
49
|
+
version: "1.2.0",
|
|
50
|
+
expectedSize: 23954240,
|
|
51
|
+
expectedChecksum: "0f250761959519bb5a28fed76ceab2cb",
|
|
52
52
|
}
|
|
53
53
|
: process.platform === "win32"
|
|
54
54
|
? {
|
|
55
|
-
version: "1.
|
|
56
|
-
expectedSize:
|
|
57
|
-
expectedChecksum: "
|
|
55
|
+
version: "1.2.0",
|
|
56
|
+
expectedSize: 24360960,
|
|
57
|
+
expectedChecksum: "168ce32c742e1d26037c52bdbb7d871c",
|
|
58
58
|
}
|
|
59
59
|
: {
|
|
60
|
-
version: "1.
|
|
61
|
-
expectedSize:
|
|
62
|
-
expectedChecksum: "
|
|
60
|
+
version: "1.2.0",
|
|
61
|
+
expectedSize: 23970052,
|
|
62
|
+
expectedChecksum: "2ca17e4009a9ebae0f7c983bafff2ee6",
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
exports.DownloadDetails = {
|
|
@@ -243,12 +243,15 @@ const Commands = {
|
|
|
243
243
|
optionalArgs: [
|
|
244
244
|
"listen",
|
|
245
245
|
"config_dir",
|
|
246
|
-
"local_connection_string",
|
|
247
246
|
"project_id",
|
|
248
247
|
"service_location",
|
|
248
|
+
"disable_sdk_generation",
|
|
249
|
+
"resolvers_emulator",
|
|
250
|
+
"vertex_location",
|
|
251
|
+
"rpc_retry_count",
|
|
249
252
|
],
|
|
250
253
|
joinArgs: true,
|
|
251
|
-
shell:
|
|
254
|
+
shell: false,
|
|
252
255
|
},
|
|
253
256
|
};
|
|
254
257
|
function getExecPath(name) {
|
|
@@ -380,7 +383,17 @@ async function _runBinary(emulator, command, extraEnv) {
|
|
|
380
383
|
});
|
|
381
384
|
}
|
|
382
385
|
function getDownloadDetails(emulator) {
|
|
383
|
-
|
|
386
|
+
const details = exports.DownloadDetails[emulator];
|
|
387
|
+
const pathOverride = process.env[`${emulator.toUpperCase()}_EMULATOR_BINARY_PATH`];
|
|
388
|
+
if (pathOverride) {
|
|
389
|
+
const logger = emulatorLogger_1.EmulatorLogger.forEmulator(emulator);
|
|
390
|
+
logger.logLabeled("WARN", emulator, `Env variable override detected. Using ${emulator} emulator at ${pathOverride}`);
|
|
391
|
+
details.downloadPath = pathOverride;
|
|
392
|
+
details.binaryPath = pathOverride;
|
|
393
|
+
details.localOnly = true;
|
|
394
|
+
fs.chmodSync(pathOverride, 0o755);
|
|
395
|
+
}
|
|
396
|
+
return details;
|
|
384
397
|
}
|
|
385
398
|
exports.getDownloadDetails = getDownloadDetails;
|
|
386
399
|
function get(emulator) {
|
|
@@ -424,7 +437,7 @@ async function downloadIfNecessary(targetName) {
|
|
|
424
437
|
}
|
|
425
438
|
exports.downloadIfNecessary = downloadIfNecessary;
|
|
426
439
|
async function start(targetName, args, extraEnv = {}) {
|
|
427
|
-
const downloadDetails =
|
|
440
|
+
const downloadDetails = getDownloadDetails(targetName);
|
|
428
441
|
const emulator = get(targetName);
|
|
429
442
|
const hasEmulator = fs.existsSync(getExecPath(targetName));
|
|
430
443
|
const logger = emulatorLogger_1.EmulatorLogger.forEmulator(targetName);
|
|
@@ -74,7 +74,7 @@ class StorageRulesRuntime {
|
|
|
74
74
|
if (this.alive) {
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
-
const downloadDetails = downloadableEmulators_1.
|
|
77
|
+
const downloadDetails = (0, downloadableEmulators_1.getDownloadDetails)(types_2.Emulators.STORAGE);
|
|
78
78
|
const hasEmulator = fs.existsSync(downloadDetails.downloadPath);
|
|
79
79
|
if (!hasEmulator) {
|
|
80
80
|
if (autoDownload) {
|
|
@@ -310,6 +310,8 @@ async function getContext(dir, targetOrConfiguration) {
|
|
|
310
310
|
continue;
|
|
311
311
|
if (target === buildTarget && builder === "@angular-devkit/build-angular:application")
|
|
312
312
|
continue;
|
|
313
|
+
if (target === buildTarget && builder === "@angular-devkit/build-angular:browser")
|
|
314
|
+
continue;
|
|
313
315
|
if (target === browserTarget && builder === "@angular-devkit/build-angular:browser-esbuild")
|
|
314
316
|
continue;
|
|
315
317
|
if (target === browserTarget && builder === "@angular-devkit/build-angular:browser")
|
|
@@ -364,10 +366,13 @@ async function getBrowserConfig(sourceDir, configuration) {
|
|
|
364
366
|
if (!buildOrBrowserTarget) {
|
|
365
367
|
throw new assert_1.AssertionError({ message: "expected build or browser target defined" });
|
|
366
368
|
}
|
|
367
|
-
const { locales, defaultLocale } = await
|
|
368
|
-
|
|
369
|
+
const [{ locales, defaultLocale }, targetOptions, builderName] = await Promise.all([
|
|
370
|
+
localesForTarget(sourceDir, architectHost, buildOrBrowserTarget, workspaceProject),
|
|
371
|
+
architectHost.getOptionsForTarget(buildOrBrowserTarget),
|
|
372
|
+
architectHost.getBuilderNameForTarget(buildOrBrowserTarget),
|
|
373
|
+
]);
|
|
369
374
|
(0, utils_2.assertIsString)(targetOptions === null || targetOptions === void 0 ? void 0 : targetOptions.outputPath);
|
|
370
|
-
const outputPath = (0, path_1.join)(targetOptions.outputPath, buildTarget ? "browser" : "");
|
|
375
|
+
const outputPath = (0, path_1.join)(targetOptions.outputPath, buildTarget && builderName === "@angular-devkit/build-angular:application" ? "browser" : "");
|
|
371
376
|
return { locales, baseHref, outputPath, defaultLocale };
|
|
372
377
|
}
|
|
373
378
|
exports.getBrowserConfig = getBrowserConfig;
|
|
@@ -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)(),
|
|
@@ -10,6 +10,8 @@ const cloudsql = require("../../../gcp/cloudsql/cloudsqladmin");
|
|
|
10
10
|
const ensureApis_1 = require("../../../dataconnect/ensureApis");
|
|
11
11
|
const client_1 = require("../../../dataconnect/client");
|
|
12
12
|
const emulators_1 = require("../emulators");
|
|
13
|
+
const names_1 = require("../../../dataconnect/names");
|
|
14
|
+
const logger_1 = require("../../../logger");
|
|
13
15
|
const TEMPLATE_ROOT = (0, path_1.resolve)(__dirname, "../../../../templates/init/dataconnect/");
|
|
14
16
|
const DATACONNECT_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "dataconnect.yaml"), "utf8");
|
|
15
17
|
const CONNECTOR_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "connector.yaml"), "utf8");
|
|
@@ -18,97 +20,21 @@ const QUERIES_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT,
|
|
|
18
20
|
const MUTATIONS_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "mutations.gql"), "utf8");
|
|
19
21
|
async function doSetup(setup, config) {
|
|
20
22
|
var _a, _b, _c;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
34
|
-
let cloudSqlInstanceId = "";
|
|
35
|
-
let newInstance = false;
|
|
36
|
-
let locationId = "";
|
|
37
|
-
if (setup.projectId) {
|
|
38
|
-
const instances = await cloudsql.listInstances(setup.projectId);
|
|
39
|
-
const choices = instances.map((i) => {
|
|
40
|
-
return { name: i.name, value: i.name, location: i.region };
|
|
41
|
-
});
|
|
42
|
-
const freeTrialInstanceId = await (0, freeTrial_1.checkForFreeTrialInstance)(setup.projectId);
|
|
43
|
-
if (!freeTrialInstanceId) {
|
|
44
|
-
choices.push({ name: "Create a new instance", value: "", location: "" });
|
|
45
|
-
}
|
|
46
|
-
if (instances.length) {
|
|
47
|
-
cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
48
|
-
message: `Which CloudSQL instance would you like to use?`,
|
|
49
|
-
type: "list",
|
|
50
|
-
choices,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
locationId = choices.find((c) => c.value === cloudSqlInstanceId).location;
|
|
54
|
-
}
|
|
55
|
-
if (cloudSqlInstanceId === "") {
|
|
56
|
-
let locationOptions = [
|
|
57
|
-
{ name: "us-central1", value: "us-central1" },
|
|
58
|
-
{ name: "europe-north1", value: "europe-north1" },
|
|
59
|
-
{ name: "europe-central2", value: "europe-central2" },
|
|
60
|
-
{ name: "europe-west1", value: "europe-west1" },
|
|
61
|
-
{ name: "southamerica-west1", value: "southamerica-west1" },
|
|
62
|
-
{ name: "us-east4", value: "us-east4" },
|
|
63
|
-
{ name: "us-west1", value: "us-west1" },
|
|
64
|
-
{ name: "asia-southeast1", value: "asia-southeast1" },
|
|
65
|
-
];
|
|
66
|
-
if (setup.projectId) {
|
|
67
|
-
const locations = await (0, client_1.listLocations)(setup.projectId);
|
|
68
|
-
locationOptions = locations.map((l) => {
|
|
69
|
-
return { name: l, value: l };
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
newInstance = true;
|
|
73
|
-
cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
74
|
-
message: `What ID would you like to use for your new CloudSQL instance?`,
|
|
75
|
-
type: "input",
|
|
76
|
-
default: `dataconnect-test`,
|
|
77
|
-
});
|
|
78
|
-
locationId = await (0, prompt_1.promptOnce)({
|
|
79
|
-
message: "What location would you use for this instance?",
|
|
80
|
-
type: "list",
|
|
81
|
-
choices: locationOptions,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
85
|
-
if (!config.has("dataconnect")) {
|
|
86
|
-
config.set("dataconnect.source", dir);
|
|
87
|
-
config.set("dataconnect.location", locationId);
|
|
88
|
-
}
|
|
89
|
-
let cloudSqlDatabase = "";
|
|
90
|
-
let newDB = false;
|
|
91
|
-
if (!newInstance && setup.projectId) {
|
|
92
|
-
const dbs = await cloudsql.listDatabases(setup.projectId, cloudSqlInstanceId);
|
|
93
|
-
const choices = dbs.map((d) => {
|
|
94
|
-
return { name: d.name, value: d.name };
|
|
95
|
-
});
|
|
96
|
-
choices.push({ name: "Create a new database", value: "" });
|
|
97
|
-
if (dbs.length) {
|
|
98
|
-
cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
99
|
-
message: `Which database in ${cloudSqlInstanceId} would you like to use?`,
|
|
100
|
-
type: "list",
|
|
101
|
-
choices,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
23
|
+
let info = {
|
|
24
|
+
serviceId: "",
|
|
25
|
+
locationId: "",
|
|
26
|
+
cloudSqlInstanceId: "",
|
|
27
|
+
isNewInstance: false,
|
|
28
|
+
cloudSqlDatabase: "",
|
|
29
|
+
isNewDatabase: false,
|
|
30
|
+
connectorId: "default-connector",
|
|
31
|
+
};
|
|
32
|
+
info = await promptForService(setup, info);
|
|
33
|
+
if (info.cloudSqlInstanceId === "") {
|
|
34
|
+
info = await promptForCloudSQLInstance(setup, info);
|
|
104
35
|
}
|
|
105
|
-
if (cloudSqlDatabase === "") {
|
|
106
|
-
|
|
107
|
-
cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
108
|
-
message: `What ID would you like to use for your new database in ${cloudSqlInstanceId}?`,
|
|
109
|
-
type: "input",
|
|
110
|
-
default: `dataconnect`,
|
|
111
|
-
});
|
|
36
|
+
if (info.cloudSqlDatabase === "") {
|
|
37
|
+
info = await promptForDatabase(setup, config, info);
|
|
112
38
|
}
|
|
113
39
|
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 : emulators_1.DEFAULT_POSTGRES_CONNECTION;
|
|
114
40
|
const localConnectionString = await (0, prompt_1.promptOnce)({
|
|
@@ -118,35 +44,27 @@ async function doSetup(setup, config) {
|
|
|
118
44
|
default: defaultConnectionString,
|
|
119
45
|
});
|
|
120
46
|
setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
cloudSqlDatabase,
|
|
125
|
-
connectorId,
|
|
126
|
-
});
|
|
127
|
-
const subbedConnectorYaml = subValues(CONNECTOR_YAML_TEMPLATE, {
|
|
128
|
-
serviceId,
|
|
129
|
-
cloudSqlInstanceId,
|
|
130
|
-
cloudSqlDatabase,
|
|
131
|
-
connectorId,
|
|
132
|
-
});
|
|
47
|
+
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
48
|
+
const subbedDataconnectYaml = subValues(DATACONNECT_YAML_TEMPLATE, info);
|
|
49
|
+
const subbedConnectorYaml = subValues(CONNECTOR_YAML_TEMPLATE, info);
|
|
133
50
|
await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
|
|
134
|
-
await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "connector.yaml"), subbedConnectorYaml);
|
|
135
51
|
await config.askWriteProjectFile((0, path_1.join)(dir, "schema", "schema.gql"), SCHEMA_TEMPLATE);
|
|
136
|
-
await config.askWriteProjectFile((0, path_1.join)(dir,
|
|
137
|
-
await config.askWriteProjectFile((0, path_1.join)(dir,
|
|
52
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "connector.yaml"), subbedConnectorYaml);
|
|
53
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "queries.gql"), QUERIES_TEMPLATE);
|
|
54
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "mutations.gql"), MUTATIONS_TEMPLATE);
|
|
138
55
|
if (setup.projectId &&
|
|
139
|
-
(
|
|
56
|
+
(info.isNewInstance || info.isNewDatabase) &&
|
|
140
57
|
(await (0, prompt_1.confirm)({
|
|
141
58
|
message: "Would you like to provision your CloudSQL instance and database now? This will take a few minutes.",
|
|
142
59
|
default: true,
|
|
143
60
|
}))) {
|
|
144
61
|
await (0, provisionCloudSql_1.provisionCloudSql)({
|
|
145
62
|
projectId: setup.projectId,
|
|
146
|
-
locationId,
|
|
147
|
-
instanceId: cloudSqlInstanceId,
|
|
148
|
-
databaseId: cloudSqlDatabase,
|
|
63
|
+
locationId: info.locationId,
|
|
64
|
+
instanceId: info.cloudSqlInstanceId,
|
|
65
|
+
databaseId: info.cloudSqlDatabase,
|
|
149
66
|
enableGoogleMlIntegration: false,
|
|
67
|
+
waitForCreation: false,
|
|
150
68
|
});
|
|
151
69
|
}
|
|
152
70
|
}
|
|
@@ -164,3 +82,146 @@ function subValues(template, replacementValues) {
|
|
|
164
82
|
}
|
|
165
83
|
return replaced;
|
|
166
84
|
}
|
|
85
|
+
async function promptForService(setup, info) {
|
|
86
|
+
var _a, _b, _c, _d;
|
|
87
|
+
if (setup.projectId) {
|
|
88
|
+
await (0, ensureApis_1.ensureApis)(setup.projectId);
|
|
89
|
+
const existingServices = await (0, client_1.listAllServices)(setup.projectId);
|
|
90
|
+
const existingServicesAndSchemas = await Promise.all(existingServices.map(async (s) => {
|
|
91
|
+
return {
|
|
92
|
+
service: s,
|
|
93
|
+
schema: await (0, client_1.getSchema)(s.name),
|
|
94
|
+
};
|
|
95
|
+
}));
|
|
96
|
+
const existingFreshServicesAndSchemas = existingServicesAndSchemas.filter((s) => {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
return !((_b = (_a = s.schema) === null || _a === void 0 ? void 0 : _a.source.files) === null || _b === void 0 ? void 0 : _b.length);
|
|
99
|
+
});
|
|
100
|
+
if (existingFreshServicesAndSchemas.length) {
|
|
101
|
+
const choices = existingFreshServicesAndSchemas.map((s) => {
|
|
102
|
+
const serviceName = (0, names_1.parseServiceName)(s.service.name);
|
|
103
|
+
return {
|
|
104
|
+
name: `${serviceName.location}/${serviceName.serviceId}`,
|
|
105
|
+
value: s,
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
choices.push({ name: "Create a new service", value: undefined });
|
|
109
|
+
const choice = await (0, prompt_1.promptOnce)({
|
|
110
|
+
message: "Your project already has existing services. Which would you like to set up local files for?",
|
|
111
|
+
type: "list",
|
|
112
|
+
choices,
|
|
113
|
+
});
|
|
114
|
+
if (choice) {
|
|
115
|
+
const serviceName = (0, names_1.parseServiceName)(choice.service.name);
|
|
116
|
+
info.serviceId = serviceName.serviceId;
|
|
117
|
+
info.locationId = serviceName.location;
|
|
118
|
+
if (choice.schema) {
|
|
119
|
+
info.cloudSqlInstanceId =
|
|
120
|
+
(_b = (_a = choice.schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) !== null && _b !== void 0 ? _b : "";
|
|
121
|
+
info.cloudSqlDatabase = (_d = (_c = choice.schema.primaryDatasource.postgresql) === null || _c === void 0 ? void 0 : _c.database) !== null && _d !== void 0 ? _d : "";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (info.serviceId === "") {
|
|
127
|
+
info.serviceId = await (0, prompt_1.promptOnce)({
|
|
128
|
+
message: "What ID would you like to use for this service?",
|
|
129
|
+
type: "input",
|
|
130
|
+
default: "my-service",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return info;
|
|
134
|
+
}
|
|
135
|
+
async function promptForCloudSQLInstance(setup, info) {
|
|
136
|
+
if (setup.projectId) {
|
|
137
|
+
const instances = await cloudsql.listInstances(setup.projectId);
|
|
138
|
+
let choices = instances.map((i) => {
|
|
139
|
+
return { name: i.name, value: i.name, location: i.region };
|
|
140
|
+
});
|
|
141
|
+
choices = choices.filter((c) => info.locationId === "" || info.locationId === c.location);
|
|
142
|
+
if (choices.length) {
|
|
143
|
+
const freeTrialInstanceId = await (0, freeTrial_1.checkForFreeTrialInstance)(setup.projectId);
|
|
144
|
+
if (!freeTrialInstanceId) {
|
|
145
|
+
choices.push({ name: "Create a new instance", value: "", location: "" });
|
|
146
|
+
}
|
|
147
|
+
info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
148
|
+
message: `Which CloudSQL instance would you like to use?`,
|
|
149
|
+
type: "list",
|
|
150
|
+
choices,
|
|
151
|
+
});
|
|
152
|
+
info.locationId = choices.find((c) => c.value === info.cloudSqlInstanceId).location;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (info.cloudSqlInstanceId === "") {
|
|
156
|
+
info.isNewInstance = true;
|
|
157
|
+
info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
158
|
+
message: `What ID would you like to use for your new CloudSQL instance?`,
|
|
159
|
+
type: "input",
|
|
160
|
+
default: `fdc-sql`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (info.locationId === "") {
|
|
164
|
+
const choices = await locationChoices(setup);
|
|
165
|
+
info.locationId = await (0, prompt_1.promptOnce)({
|
|
166
|
+
message: "What location would like to use?",
|
|
167
|
+
type: "list",
|
|
168
|
+
choices,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return info;
|
|
172
|
+
}
|
|
173
|
+
async function locationChoices(setup) {
|
|
174
|
+
if (setup.projectId) {
|
|
175
|
+
const locations = await (0, client_1.listLocations)(setup.projectId);
|
|
176
|
+
return locations.map((l) => {
|
|
177
|
+
return { name: l, value: l };
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
return [
|
|
182
|
+
{ name: "us-central1", value: "us-central1" },
|
|
183
|
+
{ name: "europe-north1", value: "europe-north1" },
|
|
184
|
+
{ name: "europe-central2", value: "europe-central2" },
|
|
185
|
+
{ name: "europe-west1", value: "europe-west1" },
|
|
186
|
+
{ name: "southamerica-west1", value: "southamerica-west1" },
|
|
187
|
+
{ name: "us-east4", value: "us-east4" },
|
|
188
|
+
{ name: "us-west1", value: "us-west1" },
|
|
189
|
+
{ name: "asia-southeast1", value: "asia-southeast1" },
|
|
190
|
+
];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function promptForDatabase(setup, config, info) {
|
|
194
|
+
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
195
|
+
if (!config.has("dataconnect")) {
|
|
196
|
+
config.set("dataconnect.source", dir);
|
|
197
|
+
config.set("dataconnect.location", info.locationId);
|
|
198
|
+
}
|
|
199
|
+
if (!info.isNewInstance && setup.projectId) {
|
|
200
|
+
try {
|
|
201
|
+
const dbs = await cloudsql.listDatabases(setup.projectId, info.cloudSqlInstanceId);
|
|
202
|
+
const choices = dbs.map((d) => {
|
|
203
|
+
return { name: d.name, value: d.name };
|
|
204
|
+
});
|
|
205
|
+
choices.push({ name: "Create a new database", value: "" });
|
|
206
|
+
if (dbs.length) {
|
|
207
|
+
info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
208
|
+
message: `Which database in ${info.cloudSqlInstanceId} would you like to use?`,
|
|
209
|
+
type: "list",
|
|
210
|
+
choices,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
logger_1.logger.debug(`[dataconnect] Cannot list databases during init: ${err}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (info.cloudSqlDatabase === "") {
|
|
219
|
+
info.isNewDatabase = true;
|
|
220
|
+
info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
221
|
+
message: `What ID would you like to use for your new database in ${info.cloudSqlInstanceId}?`,
|
|
222
|
+
type: "input",
|
|
223
|
+
default: `fdcdb`,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return info;
|
|
227
|
+
}
|
package/lib/rc.js
CHANGED
|
@@ -157,9 +157,7 @@ class RC {
|
|
|
157
157
|
return (_a = this.data.dataconnectEmulatorConfig) !== null && _a !== void 0 ? _a : {};
|
|
158
158
|
}
|
|
159
159
|
setDataconnect(localConnectionString) {
|
|
160
|
-
|
|
161
|
-
this.data.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
|
|
162
|
-
}
|
|
160
|
+
this.data.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
|
|
163
161
|
}
|
|
164
162
|
save() {
|
|
165
163
|
if (this.path) {
|
package/lib/track.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.track = exports.cliSession = exports.emulatorSession = exports.trackEmulator = exports.trackGA4 = exports.usageEnabled = exports.GA4_PROPERTIES = void 0;
|
|
3
|
+
exports.track = exports.cliSession = exports.vscodeSession = exports.emulatorSession = exports.trackVSCode = exports.trackEmulator = exports.trackGA4 = exports.usageEnabled = exports.GA4_PROPERTIES = void 0;
|
|
4
4
|
const node_fetch_1 = require("node-fetch");
|
|
5
5
|
const ua = require("universal-analytics");
|
|
6
6
|
const uuid_1 = require("uuid");
|
|
@@ -19,6 +19,11 @@ exports.GA4_PROPERTIES = {
|
|
|
19
19
|
apiSecret: process.env.FIREBASE_EMULATOR_GA4_API_SECRET || "2V_zBYc4TdeoppzDaIu0zw",
|
|
20
20
|
clientIdKey: "emulator-analytics-clientId",
|
|
21
21
|
},
|
|
22
|
+
vscode: {
|
|
23
|
+
measurementId: process.env.FIREBASE_VSCODE_GA4_MEASUREMENT_ID || "G-FYJ489XM2T",
|
|
24
|
+
apiSecret: process.env.FIREBASE_VSCODE_GA4_API_SECRET || "XAEWKHe7RM-ygCK44N52Ww",
|
|
25
|
+
clientIdKey: "vscode-analytics-clientId",
|
|
26
|
+
},
|
|
22
27
|
};
|
|
23
28
|
function usageEnabled() {
|
|
24
29
|
return !!process.env.IS_FIREBASE_CLI && !!configstore_1.configstore.get("usage");
|
|
@@ -69,6 +74,24 @@ async function trackEmulator(eventName, params) {
|
|
|
69
74
|
});
|
|
70
75
|
}
|
|
71
76
|
exports.trackEmulator = trackEmulator;
|
|
77
|
+
async function trackVSCode(eventName, params) {
|
|
78
|
+
const session = vscodeSession();
|
|
79
|
+
if (!session) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
session.debugMode = process.env.VSCODE_DEBUG_MODE === "true";
|
|
83
|
+
const oldTotalEngagementSeconds = session.totalEngagementSeconds;
|
|
84
|
+
session.totalEngagementSeconds = process.uptime();
|
|
85
|
+
const duration = session.totalEngagementSeconds - oldTotalEngagementSeconds;
|
|
86
|
+
return _ga4Track({
|
|
87
|
+
session,
|
|
88
|
+
apiSecret: exports.GA4_PROPERTIES.vscode.apiSecret,
|
|
89
|
+
eventName,
|
|
90
|
+
params,
|
|
91
|
+
duration,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
exports.trackVSCode = trackVSCode;
|
|
72
95
|
async function _ga4Track(args) {
|
|
73
96
|
const { session, apiSecret, eventName, params, duration } = args;
|
|
74
97
|
session.commandName = (params === null || params === void 0 ? void 0 : params.command_name) || session.commandName;
|
|
@@ -119,13 +142,17 @@ function emulatorSession() {
|
|
|
119
142
|
return session("emulator");
|
|
120
143
|
}
|
|
121
144
|
exports.emulatorSession = emulatorSession;
|
|
145
|
+
function vscodeSession() {
|
|
146
|
+
return session("vscode");
|
|
147
|
+
}
|
|
148
|
+
exports.vscodeSession = vscodeSession;
|
|
122
149
|
function cliSession() {
|
|
123
150
|
return session("cli");
|
|
124
151
|
}
|
|
125
152
|
exports.cliSession = cliSession;
|
|
126
153
|
function session(propertyName) {
|
|
127
154
|
const validateOnly = !!process.env.FIREBASE_CLI_MP_VALIDATE;
|
|
128
|
-
if (!usageEnabled()) {
|
|
155
|
+
if (!usageEnabled() && propertyName !== "vscode") {
|
|
129
156
|
if (validateOnly) {
|
|
130
157
|
logger_1.logger.warn("Google Analytics is DISABLED. To enable, (re)login and opt in to collection.");
|
|
131
158
|
}
|
package/package.json
CHANGED
|
@@ -726,6 +726,7 @@
|
|
|
726
726
|
"nodejs16",
|
|
727
727
|
"nodejs18",
|
|
728
728
|
"nodejs20",
|
|
729
|
+
"nodejs22",
|
|
729
730
|
"nodejs6",
|
|
730
731
|
"nodejs8",
|
|
731
732
|
"python310",
|
|
@@ -787,6 +788,7 @@
|
|
|
787
788
|
"nodejs16",
|
|
788
789
|
"nodejs18",
|
|
789
790
|
"nodejs20",
|
|
791
|
+
"nodejs22",
|
|
790
792
|
"nodejs6",
|
|
791
793
|
"nodejs8",
|
|
792
794
|
"python310",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# }
|
|
11
11
|
# mutation CreateEmail($content: String, $subject: String, $fromUid: String) @auth(level: PUBLIC) {
|
|
12
12
|
# email_insert(data: {
|
|
13
|
-
# text: $content,
|
|
13
|
+
# text: $content, # The request variable name doesn't have to match the field name.
|
|
14
14
|
# subject: $subject,
|
|
15
15
|
# fromUid: $fromUid,
|
|
16
16
|
## Server values let your service populate data for you
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
## where allows you to filter lists
|
|
14
14
|
## Here, we use it to filter to only emails where this user is one of the recipients.
|
|
15
15
|
# emails(where: {
|
|
16
|
-
# users_via_Recipient: {
|
|
16
|
+
# users_via_Recipient: {
|
|
17
17
|
# exist: { uid: { eq: $uid }
|
|
18
18
|
# }}
|
|
19
19
|
# }) {
|
|
20
|
-
# id subject sent
|
|
21
|
-
# content: text
|
|
20
|
+
# id subject sent
|
|
21
|
+
# content: text # Select the `text` field but alias it as `content` in the response.
|
|
22
22
|
# sender: from { name email: address uid }
|
|
23
23
|
|
|
24
24
|
## <field>_on_<foreign_key_field> makes it easy to grab info from another table
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
# query ListSent(
|
|
39
39
|
# $uid: String
|
|
40
40
|
# ) @auth(level: PUBLIC) {
|
|
41
|
-
# emails(where: {
|
|
41
|
+
# emails(where: {
|
|
42
42
|
# fromUid: { eq: $uid }
|
|
43
43
|
# }) {
|
|
44
|
-
# id subject sent
|
|
45
|
-
# content: text
|
|
44
|
+
# id subject sent
|
|
45
|
+
# content: text
|
|
46
46
|
# sender: from { name email: address uid }
|
|
47
47
|
# to: recipients_on_email {
|
|
48
48
|
# user { name email: address uid }
|