firebase-tools 13.7.4 → 13.8.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/api.js +9 -1
- package/lib/apiv2.js +19 -13
- package/lib/apphosting/app.js +4 -3
- package/lib/apphosting/githubConnections.js +1 -1
- package/lib/apphosting/index.js +70 -31
- package/lib/checkValidTargetFilters.js +8 -1
- package/lib/commands/apphosting-backends-create.js +3 -3
- package/lib/commands/apphosting-backends-delete.js +24 -17
- package/lib/commands/apphosting-backends-list.js +3 -3
- package/lib/commands/apphosting-secrets-grantaccess.js +9 -5
- package/lib/commands/dataconnect-list.js +64 -0
- package/lib/commands/dataconnect-sdk-generate.js +36 -0
- package/lib/commands/dataconnect-sql-diff.js +25 -0
- package/lib/commands/dataconnect-sql-migrate.js +41 -0
- package/lib/commands/deploy.js +27 -1
- package/lib/commands/index.js +10 -0
- package/lib/commands/init.js +7 -0
- package/lib/commands/setup-emulators-dataconnect.js +12 -0
- package/lib/config.js +1 -0
- package/lib/dataconnect/build.js +23 -0
- package/lib/dataconnect/checkIam.js +30 -0
- package/lib/dataconnect/client.js +115 -0
- package/lib/dataconnect/dataplaneClient.js +16 -0
- package/lib/dataconnect/ensureApis.js +12 -0
- package/lib/dataconnect/fileUtils.js +89 -0
- package/lib/dataconnect/filters.js +45 -0
- package/lib/dataconnect/freeTrial.js +23 -0
- package/lib/dataconnect/graphqlError.js +13 -0
- package/lib/dataconnect/load.js +40 -0
- package/lib/dataconnect/names.js +48 -0
- package/lib/dataconnect/prompts.js +20 -0
- package/lib/dataconnect/provisionCloudSql.js +91 -0
- package/lib/dataconnect/schemaMigration.js +137 -0
- package/lib/dataconnect/types.js +23 -0
- package/lib/deploy/dataconnect/deploy.js +84 -0
- package/lib/deploy/dataconnect/index.js +9 -0
- package/lib/deploy/dataconnect/prepare.js +30 -0
- package/lib/deploy/dataconnect/release.js +67 -0
- package/lib/deploy/functions/checkIam.js +4 -34
- package/lib/deploy/index.js +2 -0
- package/lib/downloadUtils.js +2 -2
- package/lib/emulator/constants.js +3 -0
- package/lib/emulator/controller.js +38 -12
- package/lib/emulator/dataconnectEmulator.js +86 -0
- package/lib/emulator/download.js +1 -1
- package/lib/emulator/downloadableEmulators.js +42 -3
- package/lib/emulator/portUtils.js +3 -2
- package/lib/emulator/registry.js +5 -0
- package/lib/emulator/types.js +3 -0
- package/lib/experiments.js +5 -0
- package/lib/extensions/emulator/specHelper.js +5 -39
- package/lib/gcp/apphosting.js +6 -1
- package/lib/gcp/cloudsql/cloudsqladmin.js +155 -0
- package/lib/gcp/cloudsql/connect.js +127 -0
- package/lib/gcp/cloudsql/fbToolsAuthClient.js +42 -0
- package/lib/gcp/cloudsql/types.js +2 -0
- package/lib/gcp/iam.js +33 -1
- package/lib/gcp/secretManager.js +1 -1
- package/lib/init/features/dataconnect/index.js +124 -0
- package/lib/init/features/emulators.js +13 -0
- package/lib/init/features/functions/index.js +15 -3
- package/lib/init/features/index.js +3 -1
- package/lib/init/index.js +1 -0
- package/lib/logger.js +22 -2
- package/lib/operation-poller.js +7 -1
- package/lib/rc.js +10 -1
- package/lib/requireAuth.js +1 -0
- package/lib/utils.js +51 -4
- package/package.json +6 -2
- package/schema/connector-yaml.json +54 -0
- package/schema/dataconnect-yaml.json +72 -0
- package/schema/firebase-config.json +103 -0
- package/templates/extensions/javascript/package.lint.json +2 -2
- package/templates/extensions/javascript/package.nolint.json +2 -2
- package/templates/extensions/typescript/package.lint.json +2 -2
- package/templates/extensions/typescript/package.nolint.json +2 -2
- package/templates/init/dataconnect/connector.yaml +2 -0
- package/templates/init/dataconnect/dataconnect.yaml +10 -0
- package/templates/init/dataconnect/mutations.gql +5 -0
- package/templates/init/dataconnect/queries.gql +7 -0
- package/templates/init/dataconnect/schema.gql +16 -0
- package/templates/init/functions/javascript/_gitignore +2 -1
- package/templates/init/functions/javascript/package.lint.json +2 -2
- package/templates/init/functions/javascript/package.nolint.json +2 -2
- package/templates/init/functions/python/_gitignore +1 -0
- package/templates/init/functions/typescript/_gitignore +1 -0
- package/templates/init/functions/typescript/package.lint.json +2 -2
- package/templates/init/functions/typescript/package.nolint.json +2 -2
|
@@ -12,14 +12,8 @@ const registry_1 = require("./registry");
|
|
|
12
12
|
const types_1 = require("./types");
|
|
13
13
|
const constants_1 = require("./constants");
|
|
14
14
|
const functionsEmulator_1 = require("./functionsEmulator");
|
|
15
|
-
const auth_1 = require("./auth");
|
|
16
|
-
const databaseEmulator_1 = require("./databaseEmulator");
|
|
17
|
-
const firestoreEmulator_1 = require("./firestoreEmulator");
|
|
18
|
-
const hostingEmulator_1 = require("./hostingEmulator");
|
|
19
|
-
const eventarcEmulator_1 = require("./eventarcEmulator");
|
|
20
15
|
const error_1 = require("../error");
|
|
21
16
|
const projectUtils_1 = require("../projectUtils");
|
|
22
|
-
const pubsubEmulator_1 = require("./pubsubEmulator");
|
|
23
17
|
const commandUtils = require("./commandUtils");
|
|
24
18
|
const hub_1 = require("./hub");
|
|
25
19
|
const hubExport_1 = require("./hubExport");
|
|
@@ -31,10 +25,9 @@ const hubClient_1 = require("./hubClient");
|
|
|
31
25
|
const prompt_1 = require("../prompt");
|
|
32
26
|
const commandUtils_1 = require("./commandUtils");
|
|
33
27
|
const fsutils_1 = require("../fsutils");
|
|
34
|
-
const storage_1 = require("./storage");
|
|
35
28
|
const config_1 = require("./storage/rules/config");
|
|
36
29
|
const getDefaultDatabaseInstance_1 = require("../getDefaultDatabaseInstance");
|
|
37
|
-
const
|
|
30
|
+
const auth_1 = require("../auth");
|
|
38
31
|
const extensionsEmulator_1 = require("./extensionsEmulator");
|
|
39
32
|
const projectConfig_1 = require("../functions/projectConfig");
|
|
40
33
|
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
@@ -42,6 +35,15 @@ const frameworks_1 = require("../frameworks");
|
|
|
42
35
|
const experiments = require("../experiments");
|
|
43
36
|
const portUtils_1 = require("./portUtils");
|
|
44
37
|
const supported_1 = require("../deploy/functions/runtimes/supported");
|
|
38
|
+
const auth_2 = require("./auth");
|
|
39
|
+
const databaseEmulator_1 = require("./databaseEmulator");
|
|
40
|
+
const eventarcEmulator_1 = require("./eventarcEmulator");
|
|
41
|
+
const dataconnectEmulator_1 = require("./dataconnectEmulator");
|
|
42
|
+
const firestoreEmulator_1 = require("./firestoreEmulator");
|
|
43
|
+
const hostingEmulator_1 = require("./hostingEmulator");
|
|
44
|
+
const pubsubEmulator_1 = require("./pubsubEmulator");
|
|
45
|
+
const storage_1 = require("./storage");
|
|
46
|
+
const fileUtils_1 = require("../dataconnect/fileUtils");
|
|
45
47
|
const START_LOGGING_EMULATOR = utils.envOverride("START_LOGGING_EMULATOR", "false", (val) => val === "true");
|
|
46
48
|
async function exportOnExit(options) {
|
|
47
49
|
const exportOnExitDir = options.exportOnExit;
|
|
@@ -346,7 +348,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
346
348
|
if (emulatorsNotRunning.length > 0 && !constants_1.Constants.isDemoProject(projectId)) {
|
|
347
349
|
functionsLogger.logLabeled("WARN", "functions", `The following emulators are not running, calls to these services from the Functions emulator will affect production: ${clc.bold(emulatorsNotRunning.join(", "))}`);
|
|
348
350
|
}
|
|
349
|
-
const account = (0,
|
|
351
|
+
const account = (0, auth_1.getProjectDefaultAccount)(options.projectRoot);
|
|
350
352
|
const functionsEmulator = new functionsEmulator_1.FunctionsEmulator({
|
|
351
353
|
projectId,
|
|
352
354
|
projectDir,
|
|
@@ -486,13 +488,13 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
486
488
|
throw new error_1.FirebaseError(`Cannot start the ${constants_1.Constants.description(types_1.Emulators.AUTH)} without a project: run 'firebase init' or provide the --project flag`);
|
|
487
489
|
}
|
|
488
490
|
const authAddr = legacyGetFirstAddr(types_1.Emulators.AUTH);
|
|
489
|
-
const authEmulator = new
|
|
491
|
+
const authEmulator = new auth_2.AuthEmulator({
|
|
490
492
|
host: authAddr.host,
|
|
491
493
|
port: authAddr.port,
|
|
492
494
|
projectId,
|
|
493
495
|
singleProjectMode: singleProjectModeEnabled
|
|
494
|
-
?
|
|
495
|
-
:
|
|
496
|
+
? auth_2.SingleProjectMode.WARNING
|
|
497
|
+
: auth_2.SingleProjectMode.NO_WARNING,
|
|
496
498
|
});
|
|
497
499
|
await startEmulator(authEmulator);
|
|
498
500
|
if (exportMetadata.auth) {
|
|
@@ -515,6 +517,30 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
515
517
|
});
|
|
516
518
|
await startEmulator(pubsubEmulator);
|
|
517
519
|
}
|
|
520
|
+
if (listenForEmulator.dataconnect) {
|
|
521
|
+
const dataConnectAddr = legacyGetFirstAddr(types_1.Emulators.DATACONNECT);
|
|
522
|
+
const config = (0, fileUtils_1.readFirebaseJson)(options.config);
|
|
523
|
+
if (!config.length) {
|
|
524
|
+
throw new error_1.FirebaseError("No Data Connect service found in firebase.json");
|
|
525
|
+
}
|
|
526
|
+
else if (config.length > 1) {
|
|
527
|
+
logger_1.logger.warn(`TODO: Add support for multiple services in the Data Connect emulator. Currently emulating first service ${config[0].source}`);
|
|
528
|
+
}
|
|
529
|
+
let configDir = config[0].source;
|
|
530
|
+
if (!path.isAbsolute(configDir)) {
|
|
531
|
+
const cwd = options.cwd || process.cwd();
|
|
532
|
+
configDir = path.resolve(path.join(cwd), configDir);
|
|
533
|
+
}
|
|
534
|
+
const dataConnectEmulator = new dataconnectEmulator_1.DataConnectEmulator({
|
|
535
|
+
host: dataConnectAddr.host,
|
|
536
|
+
port: dataConnectAddr.port,
|
|
537
|
+
projectId,
|
|
538
|
+
auto_download: true,
|
|
539
|
+
configDir,
|
|
540
|
+
rc: options.rc,
|
|
541
|
+
});
|
|
542
|
+
await startEmulator(dataConnectEmulator);
|
|
543
|
+
}
|
|
518
544
|
if (listenForEmulator.storage) {
|
|
519
545
|
const storageAddr = legacyGetFirstAddr(types_1.Emulators.STORAGE);
|
|
520
546
|
const storageEmulator = new storage_1.StorageEmulator({
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DataConnectEmulator = void 0;
|
|
4
|
+
const childProcess = require("child_process");
|
|
5
|
+
const api_1 = require("../api");
|
|
6
|
+
const constants_1 = require("./constants");
|
|
7
|
+
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
8
|
+
const types_1 = require("./types");
|
|
9
|
+
const error_1 = require("../error");
|
|
10
|
+
const emulatorLogger_1 = require("./emulatorLogger");
|
|
11
|
+
const types_2 = require("../dataconnect/types");
|
|
12
|
+
class DataConnectEmulator {
|
|
13
|
+
constructor(args) {
|
|
14
|
+
this.args = args;
|
|
15
|
+
this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT);
|
|
16
|
+
}
|
|
17
|
+
async start() {
|
|
18
|
+
const port = this.args.port || constants_1.Constants.getDefaultPort(types_1.Emulators.DATACONNECT);
|
|
19
|
+
this.logger.log("DEBUG", `Using Postgres connection string: ${this.getLocalConectionString()}`);
|
|
20
|
+
const info = await this.build();
|
|
21
|
+
if ((0, types_2.requiresVector)(info.metadata)) {
|
|
22
|
+
if (constants_1.Constants.isDemoProject(this.args.projectId)) {
|
|
23
|
+
this.logger.logLabeled("WARN", "Data Connect", "Detected a 'demo-' project, but vector embeddings require a real project. Operations that use vector_embed will fail.");
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.logger.logLabeled("WARN", "Data Connect", "Operations that use vector_embed will make calls to production Vertex AI");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, Object.assign(Object.assign({}, this.args), { http_port: port, grpc_port: port + 1, config_dir: this.args.configDir, local_connection_string: this.getLocalConectionString(), project_id: this.args.projectId }));
|
|
30
|
+
}
|
|
31
|
+
connect() {
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
stop() {
|
|
35
|
+
return (0, downloadableEmulators_1.stop)(types_1.Emulators.DATACONNECT);
|
|
36
|
+
}
|
|
37
|
+
getInfo() {
|
|
38
|
+
const host = this.args.host || constants_1.Constants.getDefaultHost();
|
|
39
|
+
const port = this.args.port || constants_1.Constants.getDefaultPort(types_1.Emulators.DATACONNECT);
|
|
40
|
+
return {
|
|
41
|
+
name: this.getName(),
|
|
42
|
+
host,
|
|
43
|
+
port,
|
|
44
|
+
pid: (0, downloadableEmulators_1.getPID)(types_1.Emulators.DATACONNECT),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
getName() {
|
|
48
|
+
return types_1.Emulators.DATACONNECT;
|
|
49
|
+
}
|
|
50
|
+
async generate(connectorId) {
|
|
51
|
+
const commandInfo = await (0, downloadableEmulators_1.downloadIfNecessary)(types_1.Emulators.DATACONNECT);
|
|
52
|
+
const cmd = [
|
|
53
|
+
"generate",
|
|
54
|
+
`--config_dir=${this.args.configDir}`,
|
|
55
|
+
`--connector_id=${connectorId}`,
|
|
56
|
+
];
|
|
57
|
+
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8" });
|
|
58
|
+
if (res.error) {
|
|
59
|
+
throw new error_1.FirebaseError(`Error starting up Data Connect emulator: ${res.error}`);
|
|
60
|
+
}
|
|
61
|
+
return res.stdout;
|
|
62
|
+
}
|
|
63
|
+
async build() {
|
|
64
|
+
var _a;
|
|
65
|
+
const commandInfo = await (0, downloadableEmulators_1.downloadIfNecessary)(types_1.Emulators.DATACONNECT);
|
|
66
|
+
const cmd = ["build", `--config_dir=${this.args.configDir}`];
|
|
67
|
+
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8" });
|
|
68
|
+
if (res.stderr) {
|
|
69
|
+
throw new error_1.FirebaseError(`Unable to build your Data Connect schema and connectors: ${res.stderr}`);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(res.stdout);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
throw new error_1.FirebaseError(`Unable to parse 'fdc build' output: ${(_a = res.stdout) !== null && _a !== void 0 ? _a : res.stderr}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
getLocalConectionString() {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
if ((0, api_1.dataConnectLocalConnString)()) {
|
|
81
|
+
return (0, api_1.dataConnectLocalConnString)();
|
|
82
|
+
}
|
|
83
|
+
return (_b = (_a = this.args.rc.getDataconnect()) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.localConnectionString;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.DataConnectEmulator = DataConnectEmulator;
|
package/lib/emulator/download.js
CHANGED
|
@@ -15,7 +15,7 @@ async function downloadEmulator(name) {
|
|
|
15
15
|
const emulator = downloadableEmulators.getDownloadDetails(name);
|
|
16
16
|
emulatorLogger_1.EmulatorLogger.forEmulator(name).logLabeled("BULLET", name, `downloading ${path.basename(emulator.downloadPath)}...`);
|
|
17
17
|
fs.ensureDirSync(emulator.opts.cacheDir);
|
|
18
|
-
const tmpfile = await downloadUtils.downloadToTmp(emulator.opts.remoteUrl);
|
|
18
|
+
const tmpfile = await downloadUtils.downloadToTmp(emulator.opts.remoteUrl, !!emulator.opts.auth);
|
|
19
19
|
if (!emulator.opts.skipChecksumAndSize) {
|
|
20
20
|
await validateSize(tmpfile, emulator.opts.expectedSize);
|
|
21
21
|
await validateChecksum(tmpfile, emulator.opts.expectedChecksum);
|
|
@@ -44,6 +44,17 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
44
44
|
expectedSize: 65611398,
|
|
45
45
|
expectedChecksum: "70bb840321423e6ae621a3ae2f314903",
|
|
46
46
|
},
|
|
47
|
+
dataconnect: process.platform === "darwin"
|
|
48
|
+
? {
|
|
49
|
+
version: "1.1.12",
|
|
50
|
+
expectedSize: 25542352,
|
|
51
|
+
expectedChecksum: "b523fe9d5506cc07480d1a571c72de9a",
|
|
52
|
+
}
|
|
53
|
+
: {
|
|
54
|
+
version: "1.1.12",
|
|
55
|
+
expectedSize: 23004624,
|
|
56
|
+
expectedChecksum: "e69f79755263b7cd77ec7cd15ef0d825",
|
|
57
|
+
},
|
|
47
58
|
};
|
|
48
59
|
exports.DownloadDetails = {
|
|
49
60
|
database: {
|
|
@@ -107,6 +118,22 @@ exports.DownloadDetails = {
|
|
|
107
118
|
namePrefix: "pubsub-emulator",
|
|
108
119
|
},
|
|
109
120
|
},
|
|
121
|
+
dataconnect: {
|
|
122
|
+
downloadPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}`),
|
|
123
|
+
version: EMULATOR_UPDATE_DETAILS.dataconnect.version,
|
|
124
|
+
binaryPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}`),
|
|
125
|
+
opts: {
|
|
126
|
+
cacheDir: CACHE_DIR,
|
|
127
|
+
remoteUrl: process.platform === "darwin"
|
|
128
|
+
? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`
|
|
129
|
+
: `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`,
|
|
130
|
+
expectedSize: EMULATOR_UPDATE_DETAILS.dataconnect.expectedSize,
|
|
131
|
+
expectedChecksum: EMULATOR_UPDATE_DETAILS.dataconnect.expectedChecksum,
|
|
132
|
+
skipChecksumAndSize: false,
|
|
133
|
+
namePrefix: "dataconnect-emulator",
|
|
134
|
+
auth: true,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
110
137
|
};
|
|
111
138
|
const EmulatorDetails = {
|
|
112
139
|
database: {
|
|
@@ -134,6 +161,11 @@ const EmulatorDetails = {
|
|
|
134
161
|
instance: null,
|
|
135
162
|
stdout: null,
|
|
136
163
|
},
|
|
164
|
+
dataconnect: {
|
|
165
|
+
name: types_1.Emulators.DATACONNECT,
|
|
166
|
+
instance: null,
|
|
167
|
+
stdout: null,
|
|
168
|
+
},
|
|
137
169
|
};
|
|
138
170
|
const Commands = {
|
|
139
171
|
database: {
|
|
@@ -197,6 +229,13 @@ const Commands = {
|
|
|
197
229
|
joinArgs: false,
|
|
198
230
|
shell: false,
|
|
199
231
|
},
|
|
232
|
+
dataconnect: {
|
|
233
|
+
binary: getExecPath(types_1.Emulators.DATACONNECT),
|
|
234
|
+
args: ["dev"],
|
|
235
|
+
optionalArgs: ["http_port", "grpc_port", "config_dir", "local_connection_string", "project_id"],
|
|
236
|
+
joinArgs: true,
|
|
237
|
+
shell: true,
|
|
238
|
+
},
|
|
200
239
|
};
|
|
201
240
|
function getExecPath(name) {
|
|
202
241
|
const details = getDownloadDetails(name);
|
|
@@ -364,10 +403,10 @@ async function stop(targetName) {
|
|
|
364
403
|
exports.stop = stop;
|
|
365
404
|
async function downloadIfNecessary(targetName) {
|
|
366
405
|
const hasEmulator = fs.existsSync(getExecPath(targetName));
|
|
367
|
-
if (hasEmulator) {
|
|
368
|
-
|
|
406
|
+
if (!hasEmulator) {
|
|
407
|
+
await (0, download_1.downloadEmulator)(targetName);
|
|
369
408
|
}
|
|
370
|
-
|
|
409
|
+
return Commands[targetName];
|
|
371
410
|
}
|
|
372
411
|
exports.downloadIfNecessary = downloadIfNecessary;
|
|
373
412
|
async function start(targetName, args, extraEnv = {}) {
|
|
@@ -132,8 +132,8 @@ async function checkListenable(arg1, port) {
|
|
|
132
132
|
}
|
|
133
133
|
exports.checkListenable = checkListenable;
|
|
134
134
|
async function waitForPortUsed(port, host) {
|
|
135
|
-
const interval =
|
|
136
|
-
const timeout =
|
|
135
|
+
const interval = 200;
|
|
136
|
+
const timeout = 5000;
|
|
137
137
|
try {
|
|
138
138
|
await tcpport.waitUntilUsedOnHost(port, host, interval, timeout);
|
|
139
139
|
}
|
|
@@ -147,6 +147,7 @@ const EMULATOR_CAN_LISTEN_ON_PRIMARY_ONLY = {
|
|
|
147
147
|
firestore: true,
|
|
148
148
|
"firestore.websocket": true,
|
|
149
149
|
pubsub: true,
|
|
150
|
+
dataconnect: true,
|
|
150
151
|
hub: false,
|
|
151
152
|
ui: false,
|
|
152
153
|
auth: true,
|
package/lib/emulator/registry.js
CHANGED
|
@@ -8,6 +8,7 @@ const constants_1 = require("./constants");
|
|
|
8
8
|
const emulatorLogger_1 = require("./emulatorLogger");
|
|
9
9
|
const utils_1 = require("../utils");
|
|
10
10
|
const apiv2_1 = require("../apiv2");
|
|
11
|
+
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
11
12
|
class EmulatorRegistry {
|
|
12
13
|
static async start(instance) {
|
|
13
14
|
const description = constants_1.Constants.description(instance.getName());
|
|
@@ -47,6 +48,7 @@ class EmulatorRegistry {
|
|
|
47
48
|
auth: 3.3,
|
|
48
49
|
storage: 3.5,
|
|
49
50
|
eventarc: 3.6,
|
|
51
|
+
dataconnect: 3.7,
|
|
50
52
|
hub: 4,
|
|
51
53
|
logging: 5,
|
|
52
54
|
};
|
|
@@ -83,6 +85,9 @@ class EmulatorRegistry {
|
|
|
83
85
|
}
|
|
84
86
|
return Object.assign(Object.assign({}, info), { host: (0, utils_1.connectableHostname)(info.host) });
|
|
85
87
|
}
|
|
88
|
+
static getDetails(emulator) {
|
|
89
|
+
return (0, downloadableEmulators_1.get)(emulator);
|
|
90
|
+
}
|
|
86
91
|
static url(emulator, req) {
|
|
87
92
|
const url = new URL("http://unknown/");
|
|
88
93
|
if (req) {
|
package/lib/emulator/types.js
CHANGED
|
@@ -15,6 +15,7 @@ var Emulators;
|
|
|
15
15
|
Emulators["STORAGE"] = "storage";
|
|
16
16
|
Emulators["EXTENSIONS"] = "extensions";
|
|
17
17
|
Emulators["EVENTARC"] = "eventarc";
|
|
18
|
+
Emulators["DATACONNECT"] = "dataconnect";
|
|
18
19
|
})(Emulators = exports.Emulators || (exports.Emulators = {}));
|
|
19
20
|
exports.DOWNLOADABLE_EMULATORS = [
|
|
20
21
|
Emulators.FIRESTORE,
|
|
@@ -22,6 +23,7 @@ exports.DOWNLOADABLE_EMULATORS = [
|
|
|
22
23
|
Emulators.PUBSUB,
|
|
23
24
|
Emulators.UI,
|
|
24
25
|
Emulators.STORAGE,
|
|
26
|
+
Emulators.DATACONNECT,
|
|
25
27
|
];
|
|
26
28
|
exports.IMPORT_EXPORT_EMULATORS = [
|
|
27
29
|
Emulators.FIRESTORE,
|
|
@@ -38,6 +40,7 @@ exports.ALL_SERVICE_EMULATORS = [
|
|
|
38
40
|
Emulators.PUBSUB,
|
|
39
41
|
Emulators.STORAGE,
|
|
40
42
|
Emulators.EVENTARC,
|
|
43
|
+
Emulators.DATACONNECT,
|
|
41
44
|
].filter((v) => v);
|
|
42
45
|
exports.EMULATORS_SUPPORTED_BY_FUNCTIONS = [
|
|
43
46
|
Emulators.FIRESTORE,
|
package/lib/experiments.js
CHANGED
|
@@ -89,6 +89,11 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
89
89
|
default: false,
|
|
90
90
|
public: false,
|
|
91
91
|
},
|
|
92
|
+
dataconnect: {
|
|
93
|
+
shortDescription: "Enable Data Connect related features.",
|
|
94
|
+
fullDescription: "Enable Data Connect related features.",
|
|
95
|
+
public: false,
|
|
96
|
+
},
|
|
92
97
|
});
|
|
93
98
|
function isValidExperiment(name) {
|
|
94
99
|
return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getRuntime = exports.DEFAULT_RUNTIME = exports.getFunctionProperties = exports.getFunctionResourcesWithParamSubstitution = exports.
|
|
4
|
-
const yaml = require("yaml");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const fs = require("fs-extra");
|
|
3
|
+
exports.getRuntime = exports.DEFAULT_RUNTIME = exports.getFunctionProperties = exports.getFunctionResourcesWithParamSubstitution = exports.readPostinstall = exports.readExtensionYaml = void 0;
|
|
7
4
|
const supported = require("../../deploy/functions/runtimes/supported");
|
|
8
5
|
const error_1 = require("../../error");
|
|
9
6
|
const extensionsHelper_1 = require("../extensionsHelper");
|
|
10
7
|
const utils_1 = require("../utils");
|
|
8
|
+
const utils_2 = require("../../utils");
|
|
11
9
|
const SPEC_FILE = "extension.yaml";
|
|
12
10
|
const POSTINSTALL_FILE = "POSTINSTALL.md";
|
|
13
11
|
const validFunctionTypes = [
|
|
@@ -15,22 +13,11 @@ const validFunctionTypes = [
|
|
|
15
13
|
"firebaseextensions.v1beta.v2function",
|
|
16
14
|
"firebaseextensions.v1beta.scheduledFunction",
|
|
17
15
|
];
|
|
18
|
-
function wrappedSafeLoad(source) {
|
|
19
|
-
try {
|
|
20
|
-
return yaml.parse(source);
|
|
21
|
-
}
|
|
22
|
-
catch (err) {
|
|
23
|
-
if (err instanceof yaml.YAMLParseError) {
|
|
24
|
-
throw new error_1.FirebaseError(`YAML Error: ${err.message}`, { original: err });
|
|
25
|
-
}
|
|
26
|
-
throw err;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
16
|
async function readExtensionYaml(directory) {
|
|
30
17
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
31
|
-
const extensionYaml = await readFileFromDirectory(directory, SPEC_FILE);
|
|
18
|
+
const extensionYaml = await (0, utils_2.readFileFromDirectory)(directory, SPEC_FILE);
|
|
32
19
|
const source = extensionYaml.source;
|
|
33
|
-
const spec = wrappedSafeLoad(source);
|
|
20
|
+
const spec = (0, utils_2.wrappedSafeLoad)(source);
|
|
34
21
|
spec.params = (_a = spec.params) !== null && _a !== void 0 ? _a : [];
|
|
35
22
|
spec.systemParams = (_b = spec.systemParams) !== null && _b !== void 0 ? _b : [];
|
|
36
23
|
spec.resources = (_c = spec.resources) !== null && _c !== void 0 ? _c : [];
|
|
@@ -44,31 +31,10 @@ async function readExtensionYaml(directory) {
|
|
|
44
31
|
}
|
|
45
32
|
exports.readExtensionYaml = readExtensionYaml;
|
|
46
33
|
async function readPostinstall(directory) {
|
|
47
|
-
const content = await readFileFromDirectory(directory, POSTINSTALL_FILE);
|
|
34
|
+
const content = await (0, utils_2.readFileFromDirectory)(directory, POSTINSTALL_FILE);
|
|
48
35
|
return content.source;
|
|
49
36
|
}
|
|
50
37
|
exports.readPostinstall = readPostinstall;
|
|
51
|
-
function readFileFromDirectory(directory, file) {
|
|
52
|
-
return new Promise((resolve, reject) => {
|
|
53
|
-
fs.readFile(path.resolve(directory, file), "utf8", (err, data) => {
|
|
54
|
-
if (err) {
|
|
55
|
-
if (err.code === "ENOENT") {
|
|
56
|
-
return reject(new error_1.FirebaseError(`Could not find "${file}" in "${directory}"`, { original: err }));
|
|
57
|
-
}
|
|
58
|
-
reject(new error_1.FirebaseError(`Failed to read file "${file}" in "${directory}"`, { original: err }));
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
resolve(data);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}).then((source) => {
|
|
65
|
-
return {
|
|
66
|
-
source,
|
|
67
|
-
sourceDirectory: directory,
|
|
68
|
-
};
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
exports.readFileFromDirectory = readFileFromDirectory;
|
|
72
38
|
function getFunctionResourcesWithParamSubstitution(extensionSpec, params) {
|
|
73
39
|
const rawResources = extensionSpec.resources.filter((resource) => validFunctionTypes.includes(resource.type));
|
|
74
40
|
return (0, extensionsHelper_1.substituteParams)(rawResources, params);
|
package/lib/gcp/apphosting.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getNextRolloutId = exports.ensureApiEnabled = exports.listLocations = exports.updateTraffic = exports.listRollouts = exports.createRollout = exports.createBuild = exports.listBuilds = exports.getBuild = exports.deleteBackend = exports.listBackends = exports.getBackend = exports.createBackend = exports.serviceAgentEmail = exports.client = exports.API_VERSION = void 0;
|
|
3
|
+
exports.getNextRolloutId = exports.ensureApiEnabled = exports.listLocations = exports.updateTraffic = exports.listRollouts = exports.createRollout = exports.createBuild = exports.listBuilds = exports.getBuild = exports.deleteBackend = exports.listBackends = exports.getBackend = exports.createBackend = exports.parseBackendName = exports.serviceAgentEmail = exports.client = exports.API_VERSION = void 0;
|
|
4
4
|
const proto = require("../gcp/proto");
|
|
5
5
|
const apiv2_1 = require("../apiv2");
|
|
6
6
|
const projectUtils_1 = require("../projectUtils");
|
|
@@ -24,6 +24,11 @@ function serviceAgentEmail(projectNumber) {
|
|
|
24
24
|
return `service-${projectNumber}@${P4SA_DOMAIN}`;
|
|
25
25
|
}
|
|
26
26
|
exports.serviceAgentEmail = serviceAgentEmail;
|
|
27
|
+
function parseBackendName(backendName) {
|
|
28
|
+
const [, projectName, , location, , id] = backendName.split("/");
|
|
29
|
+
return { projectName, location, id };
|
|
30
|
+
}
|
|
31
|
+
exports.parseBackendName = parseBackendName;
|
|
27
32
|
async function createBackend(projectId, location, backendReqBoby, backendId) {
|
|
28
33
|
const res = await exports.client.post(`projects/${projectId}/locations/${location}/backends`, Object.assign(Object.assign({}, backendReqBoby), { labels: Object.assign(Object.assign({}, backendReqBoby.labels), deploymentTool.labels()) }), { queryParams: { backendId } });
|
|
29
34
|
return res.body;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
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;
|
|
4
|
+
const apiv2_1 = require("../../apiv2");
|
|
5
|
+
const api_1 = require("../../api");
|
|
6
|
+
const operationPoller = require("../../operation-poller");
|
|
7
|
+
const API_VERSION = "v1";
|
|
8
|
+
const client = new apiv2_1.Client({
|
|
9
|
+
urlPrefix: (0, api_1.cloudSQLAdminOrigin)(),
|
|
10
|
+
auth: true,
|
|
11
|
+
apiVersion: API_VERSION,
|
|
12
|
+
});
|
|
13
|
+
async function listInstances(projectId) {
|
|
14
|
+
var _a;
|
|
15
|
+
const res = await client.get(`projects/${projectId}/instances`);
|
|
16
|
+
return (_a = res.body.items) !== null && _a !== void 0 ? _a : [];
|
|
17
|
+
}
|
|
18
|
+
exports.listInstances = listInstances;
|
|
19
|
+
async function getInstance(projectId, instanceId) {
|
|
20
|
+
const res = await client.get(`projects/${projectId}/instances/${instanceId}`);
|
|
21
|
+
return res.body;
|
|
22
|
+
}
|
|
23
|
+
exports.getInstance = getInstance;
|
|
24
|
+
async function createInstance(projectId, location, instanceId, enableGoogleMlIntegration) {
|
|
25
|
+
const databaseFlags = [{ name: "cloudsql.iam_authentication", value: "on" }];
|
|
26
|
+
if (enableGoogleMlIntegration) {
|
|
27
|
+
databaseFlags.push({ name: "cloudsql.enable_google_ml_integration", value: "on" });
|
|
28
|
+
}
|
|
29
|
+
const op = await client.post(`projects/${projectId}/instances`, {
|
|
30
|
+
name: instanceId,
|
|
31
|
+
region: location,
|
|
32
|
+
databaseVersion: "POSTGRES_15",
|
|
33
|
+
settings: {
|
|
34
|
+
tier: "db-f1-micro",
|
|
35
|
+
edition: "ENTERPRISE",
|
|
36
|
+
ipConfiguration: {
|
|
37
|
+
authorizedNetworks: [],
|
|
38
|
+
},
|
|
39
|
+
enableGoogleMlIntegration,
|
|
40
|
+
databaseFlags,
|
|
41
|
+
storageAutoResize: false,
|
|
42
|
+
userLabels: { "firebase-data-connect": "ft" },
|
|
43
|
+
insightsConfig: {
|
|
44
|
+
queryInsightsEnabled: true,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
const opName = `projects/${projectId}/operations/${op.body.name}`;
|
|
49
|
+
const pollRes = await operationPoller.pollOperation({
|
|
50
|
+
apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
|
|
51
|
+
apiVersion: API_VERSION,
|
|
52
|
+
operationResourceName: opName,
|
|
53
|
+
doneFn: (op) => op.status === "DONE",
|
|
54
|
+
masterTimeout: 1200000,
|
|
55
|
+
});
|
|
56
|
+
return pollRes;
|
|
57
|
+
}
|
|
58
|
+
exports.createInstance = createInstance;
|
|
59
|
+
async function updateInstanceForDataConnect(instance, enableGoogleMlIntegration) {
|
|
60
|
+
let dbFlags = setDatabaseFlag({ name: "cloudsql.iam_authentication", value: "on" }, instance.settings.databaseFlags);
|
|
61
|
+
if (enableGoogleMlIntegration) {
|
|
62
|
+
dbFlags = setDatabaseFlag({ name: "cloudsql.enable_google_ml_integration", value: "on" }, dbFlags);
|
|
63
|
+
}
|
|
64
|
+
const op = await client.patch(`projects/${instance.project}/instances/${instance.name}`, {
|
|
65
|
+
settings: {
|
|
66
|
+
ipConfiguration: {
|
|
67
|
+
ipv4Enabled: true,
|
|
68
|
+
},
|
|
69
|
+
databaseFlags: dbFlags,
|
|
70
|
+
enableGoogleMlIntegration,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
const opName = `projects/${instance.project}/operations/${op.body.name}`;
|
|
74
|
+
const pollRes = await operationPoller.pollOperation({
|
|
75
|
+
apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
|
|
76
|
+
apiVersion: API_VERSION,
|
|
77
|
+
operationResourceName: opName,
|
|
78
|
+
doneFn: (op) => op.status === "DONE",
|
|
79
|
+
masterTimeout: 1200000,
|
|
80
|
+
});
|
|
81
|
+
return pollRes;
|
|
82
|
+
}
|
|
83
|
+
exports.updateInstanceForDataConnect = updateInstanceForDataConnect;
|
|
84
|
+
function setDatabaseFlag(flag, flags = []) {
|
|
85
|
+
const temp = flags.filter((f) => f.name !== flag.name);
|
|
86
|
+
temp.push(flag);
|
|
87
|
+
return temp;
|
|
88
|
+
}
|
|
89
|
+
async function listDatabases(projectId, instanceId) {
|
|
90
|
+
const res = await client.get(`projects/${projectId}/instances/${instanceId}/databases`);
|
|
91
|
+
return res.body.items;
|
|
92
|
+
}
|
|
93
|
+
exports.listDatabases = listDatabases;
|
|
94
|
+
async function getDatabase(projectId, instanceId, databaseId) {
|
|
95
|
+
const res = await client.get(`projects/${projectId}/instances/${instanceId}/databases/${databaseId}`);
|
|
96
|
+
return res.body;
|
|
97
|
+
}
|
|
98
|
+
exports.getDatabase = getDatabase;
|
|
99
|
+
async function createDatabase(projectId, instanceId, databaseId) {
|
|
100
|
+
const op = await client.post(`projects/${projectId}/instances/${instanceId}/databases`, {
|
|
101
|
+
project: projectId,
|
|
102
|
+
instance: instanceId,
|
|
103
|
+
name: databaseId,
|
|
104
|
+
});
|
|
105
|
+
const opName = `projects/${projectId}/operations/${op.body.name}`;
|
|
106
|
+
const pollRes = await operationPoller.pollOperation({
|
|
107
|
+
apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
|
|
108
|
+
apiVersion: API_VERSION,
|
|
109
|
+
operationResourceName: opName,
|
|
110
|
+
doneFn: (op) => op.status === "DONE",
|
|
111
|
+
});
|
|
112
|
+
return pollRes;
|
|
113
|
+
}
|
|
114
|
+
exports.createDatabase = createDatabase;
|
|
115
|
+
async function createUser(projectId, instanceId, type, username, password) {
|
|
116
|
+
const op = await client.post(`projects/${projectId}/instances/${instanceId}/users`, {
|
|
117
|
+
name: username,
|
|
118
|
+
instance: instanceId,
|
|
119
|
+
project: projectId,
|
|
120
|
+
password: password,
|
|
121
|
+
sqlserverUserDetails: {
|
|
122
|
+
disabled: false,
|
|
123
|
+
serverRoles: ["cloudsqlsuperuser"],
|
|
124
|
+
},
|
|
125
|
+
type,
|
|
126
|
+
});
|
|
127
|
+
const opName = `projects/${projectId}/operations/${op.body.name}`;
|
|
128
|
+
const pollRes = await operationPoller.pollOperation({
|
|
129
|
+
apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
|
|
130
|
+
apiVersion: API_VERSION,
|
|
131
|
+
operationResourceName: opName,
|
|
132
|
+
doneFn: (op) => op.status === "DONE",
|
|
133
|
+
});
|
|
134
|
+
return pollRes;
|
|
135
|
+
}
|
|
136
|
+
exports.createUser = createUser;
|
|
137
|
+
async function getUser(projectId, instanceId, username) {
|
|
138
|
+
const res = await client.get(`projects/${projectId}/instances/${instanceId}/users/${username}`);
|
|
139
|
+
return res.body;
|
|
140
|
+
}
|
|
141
|
+
exports.getUser = getUser;
|
|
142
|
+
async function deleteUser(projectId, instanceId, username) {
|
|
143
|
+
const res = await client.delete(`projects/${projectId}/instances/${instanceId}/users`, {
|
|
144
|
+
queryParams: {
|
|
145
|
+
name: username,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
return res.body;
|
|
149
|
+
}
|
|
150
|
+
exports.deleteUser = deleteUser;
|
|
151
|
+
async function listUsers(projectId, instanceId) {
|
|
152
|
+
const res = await client.get(`projects/${projectId}/instances/${instanceId}/users`);
|
|
153
|
+
return res.body.items;
|
|
154
|
+
}
|
|
155
|
+
exports.listUsers = listUsers;
|