firebase-tools 13.10.1 → 13.11.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/commands/dataconnect-services-list.js +6 -12
- package/lib/crashlytics/buildToolsJarHelper.js +2 -1
- package/lib/dataconnect/client.js +10 -2
- package/lib/dataconnect/graphqlError.js +7 -4
- package/lib/dataconnect/schemaMigration.js +2 -8
- 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 +45 -11
- package/lib/emulator/download.js +4 -0
- package/lib/emulator/downloadableEmulators.js +42 -21
- package/lib/emulator/storage/rules/runtime.js +1 -1
- package/lib/frameworks/angular/utils.js +8 -3
- package/lib/init/features/dataconnect/index.js +159 -105
- 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`);
|
|
@@ -66,8 +66,16 @@ async function deleteService(projectId, locationId, serviceId) {
|
|
|
66
66
|
}
|
|
67
67
|
exports.deleteService = deleteService;
|
|
68
68
|
async function getSchema(serviceName) {
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
try {
|
|
70
|
+
const res = await dataconnectClient().get(`${serviceName}/schemas/${types.SCHEMA_ID}`);
|
|
71
|
+
return res.body;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err.status !== 404) {
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
71
79
|
}
|
|
72
80
|
exports.getSchema = getSchema;
|
|
73
81
|
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
|
}
|
|
@@ -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
|
+
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,24 @@ 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 = registry_1.EmulatorRegistry.client(types_1.Emulators.DATACONNECT);
|
|
122
|
+
}
|
|
123
|
+
async configureEmulator(body) {
|
|
124
|
+
const res = await this.client.post("emulator/configure", body);
|
|
125
|
+
return res;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
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,21 +40,27 @@ 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
|
-
:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
: process.platform === "win32"
|
|
54
|
+
? {
|
|
55
|
+
version: "1.2.0",
|
|
56
|
+
expectedSize: 24360960,
|
|
57
|
+
expectedChecksum: "168ce32c742e1d26037c52bdbb7d871c",
|
|
58
|
+
}
|
|
59
|
+
: {
|
|
60
|
+
version: "1.2.0",
|
|
61
|
+
expectedSize: 23970052,
|
|
62
|
+
expectedChecksum: "2ca17e4009a9ebae0f7c983bafff2ee6",
|
|
63
|
+
},
|
|
58
64
|
};
|
|
59
65
|
exports.DownloadDetails = {
|
|
60
66
|
database: {
|
|
@@ -119,14 +125,16 @@ exports.DownloadDetails = {
|
|
|
119
125
|
},
|
|
120
126
|
},
|
|
121
127
|
dataconnect: {
|
|
122
|
-
downloadPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}`),
|
|
128
|
+
downloadPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}${process.platform === "win32" ? ".exe" : ""}`),
|
|
123
129
|
version: EMULATOR_UPDATE_DETAILS.dataconnect.version,
|
|
124
|
-
binaryPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}`),
|
|
130
|
+
binaryPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}${process.platform === "win32" ? ".exe" : ""}`),
|
|
125
131
|
opts: {
|
|
126
132
|
cacheDir: CACHE_DIR,
|
|
127
133
|
remoteUrl: process.platform === "darwin"
|
|
128
134
|
? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`
|
|
129
|
-
:
|
|
135
|
+
: process.platform === "win32"
|
|
136
|
+
? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`
|
|
137
|
+
: `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`,
|
|
130
138
|
expectedSize: EMULATOR_UPDATE_DETAILS.dataconnect.expectedSize,
|
|
131
139
|
expectedChecksum: EMULATOR_UPDATE_DETAILS.dataconnect.expectedChecksum,
|
|
132
140
|
skipChecksumAndSize: false,
|
|
@@ -235,12 +243,15 @@ const Commands = {
|
|
|
235
243
|
optionalArgs: [
|
|
236
244
|
"listen",
|
|
237
245
|
"config_dir",
|
|
238
|
-
"local_connection_string",
|
|
239
246
|
"project_id",
|
|
240
247
|
"service_location",
|
|
248
|
+
"disable_sdk_generation",
|
|
249
|
+
"resolvers_emulator",
|
|
250
|
+
"vertex_location",
|
|
251
|
+
"rpc_retry_count",
|
|
241
252
|
],
|
|
242
253
|
joinArgs: true,
|
|
243
|
-
shell:
|
|
254
|
+
shell: false,
|
|
244
255
|
},
|
|
245
256
|
};
|
|
246
257
|
function getExecPath(name) {
|
|
@@ -372,7 +383,17 @@ async function _runBinary(emulator, command, extraEnv) {
|
|
|
372
383
|
});
|
|
373
384
|
}
|
|
374
385
|
function getDownloadDetails(emulator) {
|
|
375
|
-
|
|
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;
|
|
376
397
|
}
|
|
377
398
|
exports.getDownloadDetails = getDownloadDetails;
|
|
378
399
|
function get(emulator) {
|
|
@@ -416,7 +437,7 @@ async function downloadIfNecessary(targetName) {
|
|
|
416
437
|
}
|
|
417
438
|
exports.downloadIfNecessary = downloadIfNecessary;
|
|
418
439
|
async function start(targetName, args, extraEnv = {}) {
|
|
419
|
-
const downloadDetails =
|
|
440
|
+
const downloadDetails = getDownloadDetails(targetName);
|
|
420
441
|
const emulator = get(targetName);
|
|
421
442
|
const hasEmulator = fs.existsSync(getExecPath(targetName));
|
|
422
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;
|
|
@@ -10,6 +10,7 @@ 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");
|
|
13
14
|
const TEMPLATE_ROOT = (0, path_1.resolve)(__dirname, "../../../../templates/init/dataconnect/");
|
|
14
15
|
const DATACONNECT_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "dataconnect.yaml"), "utf8");
|
|
15
16
|
const CONNECTOR_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "connector.yaml"), "utf8");
|
|
@@ -18,42 +19,164 @@ const QUERIES_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT,
|
|
|
18
19
|
const MUTATIONS_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "mutations.gql"), "utf8");
|
|
19
20
|
async function doSetup(setup, config) {
|
|
20
21
|
var _a, _b, _c;
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
let info = {
|
|
23
|
+
serviceId: "",
|
|
24
|
+
locationId: "",
|
|
25
|
+
cloudSqlInstanceId: "",
|
|
26
|
+
isNewInstance: false,
|
|
27
|
+
cloudSqlDatabase: "",
|
|
28
|
+
isNewDatabase: false,
|
|
29
|
+
connectorId: "default-connector",
|
|
30
|
+
};
|
|
31
|
+
info = await promptForService(setup, info);
|
|
32
|
+
if (info.cloudSqlInstanceId === "") {
|
|
33
|
+
info = await promptForCloudSQLInstance(setup, info);
|
|
23
34
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const connectorId = await (0, prompt_1.promptOnce)({
|
|
30
|
-
message: "What ID would you like to use for your connector?",
|
|
35
|
+
if (info.cloudSqlDatabase === "") {
|
|
36
|
+
info = await promptForDatabase(setup, config, info);
|
|
37
|
+
}
|
|
38
|
+
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;
|
|
39
|
+
const localConnectionString = await (0, prompt_1.promptOnce)({
|
|
31
40
|
type: "input",
|
|
32
|
-
|
|
41
|
+
name: "localConnectionString",
|
|
42
|
+
message: `What is the connection string of the local Postgres instance you would like to use with the Data Connect emulator?`,
|
|
43
|
+
default: defaultConnectionString,
|
|
33
44
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
|
|
46
|
+
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
47
|
+
const subbedDataconnectYaml = subValues(DATACONNECT_YAML_TEMPLATE, info);
|
|
48
|
+
const subbedConnectorYaml = subValues(CONNECTOR_YAML_TEMPLATE, info);
|
|
49
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
|
|
50
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, "schema", "schema.gql"), SCHEMA_TEMPLATE);
|
|
51
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "connector.yaml"), subbedConnectorYaml);
|
|
52
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "queries.gql"), QUERIES_TEMPLATE);
|
|
53
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "mutations.gql"), MUTATIONS_TEMPLATE);
|
|
54
|
+
if (setup.projectId &&
|
|
55
|
+
(info.isNewInstance || info.isNewDatabase) &&
|
|
56
|
+
(await (0, prompt_1.confirm)({
|
|
57
|
+
message: "Would you like to provision your CloudSQL instance and database now? This will take a few minutes.",
|
|
58
|
+
default: true,
|
|
59
|
+
}))) {
|
|
60
|
+
await (0, provisionCloudSql_1.provisionCloudSql)({
|
|
61
|
+
projectId: setup.projectId,
|
|
62
|
+
locationId: info.locationId,
|
|
63
|
+
instanceId: info.cloudSqlInstanceId,
|
|
64
|
+
databaseId: info.cloudSqlDatabase,
|
|
65
|
+
enableGoogleMlIntegration: false,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.doSetup = doSetup;
|
|
70
|
+
function subValues(template, replacementValues) {
|
|
71
|
+
const replacements = {
|
|
72
|
+
serviceId: "__serviceId__",
|
|
73
|
+
cloudSqlDatabase: "__cloudSqlDatabase__",
|
|
74
|
+
cloudSqlInstanceId: "__cloudSqlInstanceId__",
|
|
75
|
+
connectorId: "__connectorId__",
|
|
76
|
+
};
|
|
77
|
+
let replaced = template;
|
|
78
|
+
for (const [k, v] of Object.entries(replacementValues)) {
|
|
79
|
+
replaced = replaced.replace(replacements[k], v);
|
|
80
|
+
}
|
|
81
|
+
return replaced;
|
|
82
|
+
}
|
|
83
|
+
async function promptForService(setup, info) {
|
|
84
|
+
var _a, _b, _c, _d;
|
|
85
|
+
if (setup.projectId) {
|
|
86
|
+
await (0, ensureApis_1.ensureApis)(setup.projectId);
|
|
87
|
+
const existingServices = await (0, client_1.listAllServices)(setup.projectId);
|
|
88
|
+
const existingServicesAndSchemas = await Promise.all(existingServices.map(async (s) => {
|
|
89
|
+
return {
|
|
90
|
+
service: s,
|
|
91
|
+
schema: await (0, client_1.getSchema)(s.name),
|
|
92
|
+
};
|
|
93
|
+
}));
|
|
94
|
+
const existingFreshServicesAndSchemas = existingServicesAndSchemas.filter((s) => {
|
|
95
|
+
var _a, _b;
|
|
96
|
+
return !((_b = (_a = s.schema) === null || _a === void 0 ? void 0 : _a.source.files) === null || _b === void 0 ? void 0 : _b.length);
|
|
97
|
+
});
|
|
98
|
+
if (existingFreshServicesAndSchemas.length) {
|
|
99
|
+
const choices = existingFreshServicesAndSchemas.map((s) => {
|
|
100
|
+
const serviceName = (0, names_1.parseServiceName)(s.service.name);
|
|
101
|
+
return {
|
|
102
|
+
name: `${serviceName.location}/${serviceName.serviceId}`,
|
|
103
|
+
value: s,
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
choices.push({ name: "Create a new service", value: undefined });
|
|
107
|
+
const choice = await (0, prompt_1.promptOnce)({
|
|
108
|
+
message: "Your project already has existing services. Which would you like to set up local files for?",
|
|
109
|
+
type: "list",
|
|
110
|
+
choices,
|
|
111
|
+
});
|
|
112
|
+
if (choice) {
|
|
113
|
+
const serviceName = (0, names_1.parseServiceName)(choice.service.name);
|
|
114
|
+
info.serviceId = serviceName.serviceId;
|
|
115
|
+
info.locationId = serviceName.location;
|
|
116
|
+
if (choice.schema) {
|
|
117
|
+
info.cloudSqlInstanceId =
|
|
118
|
+
(_b = (_a = choice.schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) !== null && _b !== void 0 ? _b : "";
|
|
119
|
+
info.cloudSqlDatabase = (_d = (_c = choice.schema.primaryDatasource.postgresql) === null || _c === void 0 ? void 0 : _c.database) !== null && _d !== void 0 ? _d : "";
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (info.serviceId === "") {
|
|
125
|
+
info.serviceId = await (0, prompt_1.promptOnce)({
|
|
126
|
+
message: "What ID would you like to use for this service?",
|
|
127
|
+
type: "input",
|
|
128
|
+
default: "my-service",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return info;
|
|
132
|
+
}
|
|
133
|
+
async function promptForCloudSQLInstance(setup, info) {
|
|
37
134
|
if (setup.projectId) {
|
|
38
135
|
const instances = await cloudsql.listInstances(setup.projectId);
|
|
39
|
-
|
|
136
|
+
let choices = instances.map((i) => {
|
|
40
137
|
return { name: i.name, value: i.name, location: i.region };
|
|
41
138
|
});
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
139
|
+
choices = choices.filter((c) => info.locationId === "" || info.locationId === c.location);
|
|
140
|
+
if (choices.length) {
|
|
141
|
+
const freeTrialInstanceId = await (0, freeTrial_1.checkForFreeTrialInstance)(setup.projectId);
|
|
142
|
+
if (!freeTrialInstanceId) {
|
|
143
|
+
choices.push({ name: "Create a new instance", value: "", location: "" });
|
|
144
|
+
}
|
|
145
|
+
info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
48
146
|
message: `Which CloudSQL instance would you like to use?`,
|
|
49
147
|
type: "list",
|
|
50
148
|
choices,
|
|
51
149
|
});
|
|
150
|
+
info.locationId = choices.find((c) => c.value === info.cloudSqlInstanceId).location;
|
|
52
151
|
}
|
|
53
|
-
locationId = choices.find((c) => c.value === cloudSqlInstanceId).location;
|
|
54
152
|
}
|
|
55
|
-
if (cloudSqlInstanceId === "") {
|
|
56
|
-
|
|
153
|
+
if (info.cloudSqlInstanceId === "") {
|
|
154
|
+
info.isNewInstance = true;
|
|
155
|
+
info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
156
|
+
message: `What ID would you like to use for your new CloudSQL instance?`,
|
|
157
|
+
type: "input",
|
|
158
|
+
default: `fdc-sql`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (info.locationId === "") {
|
|
162
|
+
const choices = await locationChoices(setup);
|
|
163
|
+
info.locationId = await (0, prompt_1.promptOnce)({
|
|
164
|
+
message: "What location would like to use?",
|
|
165
|
+
type: "list",
|
|
166
|
+
choices,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return info;
|
|
170
|
+
}
|
|
171
|
+
async function locationChoices(setup) {
|
|
172
|
+
if (setup.projectId) {
|
|
173
|
+
const locations = await (0, client_1.listLocations)(setup.projectId);
|
|
174
|
+
return locations.map((l) => {
|
|
175
|
+
return { name: l, value: l };
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
return [
|
|
57
180
|
{ name: "us-central1", value: "us-central1" },
|
|
58
181
|
{ name: "europe-north1", value: "europe-north1" },
|
|
59
182
|
{ name: "europe-central2", value: "europe-central2" },
|
|
@@ -63,104 +186,35 @@ async function doSetup(setup, config) {
|
|
|
63
186
|
{ name: "us-west1", value: "us-west1" },
|
|
64
187
|
{ name: "asia-southeast1", value: "asia-southeast1" },
|
|
65
188
|
];
|
|
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
189
|
}
|
|
190
|
+
}
|
|
191
|
+
async function promptForDatabase(setup, config, info) {
|
|
84
192
|
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
85
193
|
if (!config.has("dataconnect")) {
|
|
86
194
|
config.set("dataconnect.source", dir);
|
|
87
|
-
config.set("dataconnect.location", locationId);
|
|
195
|
+
config.set("dataconnect.location", info.locationId);
|
|
88
196
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (!newInstance && setup.projectId) {
|
|
92
|
-
const dbs = await cloudsql.listDatabases(setup.projectId, cloudSqlInstanceId);
|
|
197
|
+
if (!info.isNewInstance && setup.projectId) {
|
|
198
|
+
const dbs = await cloudsql.listDatabases(setup.projectId, info.cloudSqlInstanceId);
|
|
93
199
|
const choices = dbs.map((d) => {
|
|
94
200
|
return { name: d.name, value: d.name };
|
|
95
201
|
});
|
|
96
202
|
choices.push({ name: "Create a new database", value: "" });
|
|
97
203
|
if (dbs.length) {
|
|
98
|
-
cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
99
|
-
message: `Which database in ${cloudSqlInstanceId} would you like to use?`,
|
|
204
|
+
info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
205
|
+
message: `Which database in ${info.cloudSqlInstanceId} would you like to use?`,
|
|
100
206
|
type: "list",
|
|
101
207
|
choices,
|
|
102
208
|
});
|
|
103
209
|
}
|
|
104
210
|
}
|
|
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}?`,
|
|
211
|
+
if (info.cloudSqlDatabase === "") {
|
|
212
|
+
info.isNewDatabase = true;
|
|
213
|
+
info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
214
|
+
message: `What ID would you like to use for your new database in ${info.cloudSqlInstanceId}?`,
|
|
109
215
|
type: "input",
|
|
110
|
-
default: `
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
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
|
-
const localConnectionString = await (0, prompt_1.promptOnce)({
|
|
115
|
-
type: "input",
|
|
116
|
-
name: "localConnectionString",
|
|
117
|
-
message: `What is the connection string of the local Postgres instance you would like to use with the Data Connect emulator?`,
|
|
118
|
-
default: defaultConnectionString,
|
|
119
|
-
});
|
|
120
|
-
setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
|
|
121
|
-
const subbedDataconnectYaml = subValues(DATACONNECT_YAML_TEMPLATE, {
|
|
122
|
-
serviceId,
|
|
123
|
-
cloudSqlInstanceId,
|
|
124
|
-
cloudSqlDatabase,
|
|
125
|
-
connectorId,
|
|
126
|
-
});
|
|
127
|
-
const subbedConnectorYaml = subValues(CONNECTOR_YAML_TEMPLATE, {
|
|
128
|
-
serviceId,
|
|
129
|
-
cloudSqlInstanceId,
|
|
130
|
-
cloudSqlDatabase,
|
|
131
|
-
connectorId,
|
|
132
|
-
});
|
|
133
|
-
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
|
-
await config.askWriteProjectFile((0, path_1.join)(dir, "schema", "schema.gql"), SCHEMA_TEMPLATE);
|
|
136
|
-
await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "queries.gql"), QUERIES_TEMPLATE);
|
|
137
|
-
await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "mutations.gql"), MUTATIONS_TEMPLATE);
|
|
138
|
-
if (setup.projectId &&
|
|
139
|
-
(newInstance || newDB) &&
|
|
140
|
-
(await (0, prompt_1.confirm)({
|
|
141
|
-
message: "Would you like to provision your CloudSQL instance and database now? This will take a few minutes.",
|
|
142
|
-
default: true,
|
|
143
|
-
}))) {
|
|
144
|
-
await (0, provisionCloudSql_1.provisionCloudSql)({
|
|
145
|
-
projectId: setup.projectId,
|
|
146
|
-
locationId,
|
|
147
|
-
instanceId: cloudSqlInstanceId,
|
|
148
|
-
databaseId: cloudSqlDatabase,
|
|
149
|
-
enableGoogleMlIntegration: false,
|
|
216
|
+
default: `fdcdb`,
|
|
150
217
|
});
|
|
151
218
|
}
|
|
152
|
-
|
|
153
|
-
exports.doSetup = doSetup;
|
|
154
|
-
function subValues(template, replacementValues) {
|
|
155
|
-
const replacements = {
|
|
156
|
-
serviceId: "__serviceId__",
|
|
157
|
-
cloudSqlDatabase: "__cloudSqlDatabase__",
|
|
158
|
-
cloudSqlInstanceId: "__cloudSqlInstanceId__",
|
|
159
|
-
connectorId: "__connectorId__",
|
|
160
|
-
};
|
|
161
|
-
let replaced = template;
|
|
162
|
-
for (const [k, v] of Object.entries(replacementValues)) {
|
|
163
|
-
replaced = replaced.replace(replacements[k], v);
|
|
164
|
-
}
|
|
165
|
-
return replaced;
|
|
219
|
+
return info;
|
|
166
220
|
}
|
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 }
|