firebase-tools 13.17.0 → 13.19.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/README.md +10 -9
- package/lib/commands/dataconnect-services-list.js +4 -3
- package/lib/commands/dataconnect-sql-grant.js +37 -0
- package/lib/commands/dataconnect-sql-migrate.js +2 -1
- package/lib/commands/deploy.js +2 -0
- package/lib/commands/ext-info.js +3 -1
- package/lib/commands/ext-sdk-install.js +88 -0
- package/lib/commands/ext.js +1 -0
- package/lib/commands/index.js +3 -0
- package/lib/dataconnect/client.js +1 -1
- package/lib/dataconnect/dataplaneClient.js +1 -1
- package/lib/dataconnect/ensureApis.js +6 -1
- package/lib/dataconnect/load.js +3 -1
- package/lib/dataconnect/provisionCloudSql.js +34 -21
- package/lib/dataconnect/schemaMigration.js +126 -73
- package/lib/deploy/dataconnect/deploy.js +16 -13
- package/lib/deploy/dataconnect/prepare.js +36 -0
- package/lib/deploy/dataconnect/release.js +9 -2
- package/lib/deploy/extensions/deploymentSummary.js +3 -2
- package/lib/deploy/extensions/planner.js +32 -3
- package/lib/deploy/extensions/prepare.js +36 -64
- package/lib/deploy/extensions/release.js +11 -10
- package/lib/deploy/extensions/tasks.js +32 -21
- package/lib/deploy/firestore/prepare.js +10 -0
- package/lib/deploy/firestore/release.js +3 -6
- package/lib/deploy/functions/checkIam.js +7 -2
- package/lib/deploy/functions/ensure.js +10 -2
- package/lib/deploy/functions/prepare.js +10 -2
- package/lib/deploy/functions/runtimes/node/index.js +1 -1
- package/lib/deploy/index.js +9 -5
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/extensions/extensionsApi.js +3 -2
- package/lib/extensions/extensionsHelper.js +3 -3
- package/lib/extensions/localHelper.js +31 -0
- package/lib/extensions/runtimes/common.js +186 -38
- package/lib/extensions/runtimes/node.js +399 -0
- package/lib/extensions/types.js +10 -14
- package/lib/extensions/warnings.js +5 -2
- package/lib/firestore/checkDatabaseType.js +10 -3
- package/lib/frameworks/angular/index.js +15 -3
- package/lib/frameworks/next/utils.js +1 -1
- package/lib/gcp/cloudsql/permissions.js +6 -1
- package/lib/gcp/secretManager.js +12 -5
- package/lib/init/features/dataconnect/index.js +66 -53
- package/lib/init/features/firestore/index.js +20 -1
- package/lib/init/features/firestore/indexes.js +4 -4
- package/package.json +1 -1
- package/schema/connector-yaml.json +43 -17
- package/templates/init/dataconnect/connector.yaml +0 -1
- package/templates/init/dataconnect/dataconnect-fdccompatiblemode.yaml +12 -0
- package/templates/init/dataconnect/dataconnect.yaml +1 -1
package/lib/extensions/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isExtensionSpec = exports.isResource = exports.isParam = exports.ParamType = exports.FUNCTIONS_V2_RESOURCE_TYPE = exports.FUNCTIONS_RESOURCE_TYPE = exports.Visibility = exports.RegistryLaunchStage = void 0;
|
|
3
|
+
exports.isExtensionSpec = exports.isResource = exports.isParam = exports.isObject = exports.ParamType = exports.FUNCTIONS_V2_RESOURCE_TYPE = exports.FUNCTIONS_RESOURCE_TYPE = exports.Visibility = exports.RegistryLaunchStage = void 0;
|
|
4
4
|
var RegistryLaunchStage;
|
|
5
5
|
(function (RegistryLaunchStage) {
|
|
6
6
|
RegistryLaunchStage["EXPERIMENTAL"] = "EXPERIMENTAL";
|
|
@@ -14,6 +14,7 @@ var Visibility;
|
|
|
14
14
|
Visibility["UNLISTED"] = "unlisted";
|
|
15
15
|
Visibility["PUBLIC"] = "public";
|
|
16
16
|
})(Visibility = exports.Visibility || (exports.Visibility = {}));
|
|
17
|
+
const lifecycleStages = ["STAGE_UNSPECIFIED", "ON_INSTALL", "ON_UPDATE", "ON_CONFIGURE"];
|
|
17
18
|
exports.FUNCTIONS_RESOURCE_TYPE = "firebaseextensions.v1beta.function";
|
|
18
19
|
exports.FUNCTIONS_V2_RESOURCE_TYPE = "firebaseextensions.v1beta.v2function";
|
|
19
20
|
var ParamType;
|
|
@@ -27,6 +28,7 @@ var ParamType;
|
|
|
27
28
|
function isObject(value) {
|
|
28
29
|
return typeof value === "object" && value !== null;
|
|
29
30
|
}
|
|
31
|
+
exports.isObject = isObject;
|
|
30
32
|
const isParam = (param) => {
|
|
31
33
|
return (isObject(param) && typeof param["param"] === "string" && typeof param["label"] === "string");
|
|
32
34
|
};
|
|
@@ -39,41 +41,35 @@ const isExtensionSpec = (spec) => {
|
|
|
39
41
|
if (!isObject(spec) || typeof spec.name !== "string" || typeof spec.version !== "string") {
|
|
40
42
|
return false;
|
|
41
43
|
}
|
|
42
|
-
let validResources = true;
|
|
43
44
|
if (spec.resources && Array.isArray(spec.resources)) {
|
|
44
45
|
for (const res of spec.resources) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
break;
|
|
46
|
+
if (!(0, exports.isResource)(res)) {
|
|
47
|
+
return false;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
else {
|
|
52
52
|
return false;
|
|
53
53
|
}
|
|
54
|
-
let validParams = true;
|
|
55
54
|
if (spec.params && Array.isArray(spec.params)) {
|
|
56
55
|
for (const param of spec.params) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
break;
|
|
56
|
+
if (!(0, exports.isParam)(param)) {
|
|
57
|
+
return false;
|
|
60
58
|
}
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
else {
|
|
64
62
|
return false;
|
|
65
63
|
}
|
|
66
|
-
let validSysParams = true;
|
|
67
64
|
if (spec.systemParams && Array.isArray(spec.systemParams)) {
|
|
68
65
|
for (const param of spec.systemParams) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
break;
|
|
66
|
+
if (!(0, exports.isParam)(param)) {
|
|
67
|
+
return false;
|
|
72
68
|
}
|
|
73
69
|
}
|
|
74
70
|
}
|
|
75
71
|
else {
|
|
76
|
-
return
|
|
72
|
+
return !spec.systemParams;
|
|
77
73
|
}
|
|
78
74
|
return true;
|
|
79
75
|
};
|
|
@@ -31,9 +31,12 @@ async function displayWarningsForDeploy(instancesToCreate) {
|
|
|
31
31
|
return unpublishedExtensions.length > 0;
|
|
32
32
|
}
|
|
33
33
|
exports.displayWarningsForDeploy = displayWarningsForDeploy;
|
|
34
|
-
function outOfBandChangesWarning(instanceIds) {
|
|
34
|
+
function outOfBandChangesWarning(instanceIds, isDynamic) {
|
|
35
|
+
const extra = isDynamic
|
|
36
|
+
? ""
|
|
37
|
+
: " To avoid this, run `firebase ext:export` to sync these changes to your local extensions manifest.";
|
|
35
38
|
logger_1.logger.warn("The following instances may have been changed in the Firebase console or by another machine since the last deploy from this machine.\n\t" +
|
|
36
39
|
clc.bold(instanceIds.join("\n\t")) +
|
|
37
|
-
|
|
40
|
+
`\nIf you proceed with this deployment, those changes will be overwritten.${extra}`);
|
|
38
41
|
}
|
|
39
42
|
exports.outOfBandChangesWarning = outOfBandChangesWarning;
|
|
@@ -4,14 +4,21 @@ exports.checkDatabaseType = void 0;
|
|
|
4
4
|
const api_1 = require("../api");
|
|
5
5
|
const apiv2_1 = require("../apiv2");
|
|
6
6
|
const logger_1 = require("../logger");
|
|
7
|
-
|
|
7
|
+
const error_1 = require("../error");
|
|
8
|
+
async function checkDatabaseType(projectId, databaseId = "(default)") {
|
|
8
9
|
try {
|
|
9
10
|
const client = new apiv2_1.Client({ urlPrefix: (0, api_1.firestoreOrigin)(), apiVersion: "v1" });
|
|
10
|
-
const resp = await client.get(`/projects/${projectId}/databases
|
|
11
|
+
const resp = await client.get(`/projects/${projectId}/databases/${databaseId}`);
|
|
11
12
|
return resp.body.type;
|
|
12
13
|
}
|
|
13
14
|
catch (err) {
|
|
14
|
-
logger_1.logger.debug("error getting database type", err);
|
|
15
|
+
logger_1.logger.debug("error getting database type: ", err);
|
|
16
|
+
if (err instanceof error_1.FirebaseError) {
|
|
17
|
+
if (err.status === 404) {
|
|
18
|
+
logger_1.logger.info(`${databaseId} does not exist in project ${projectId}.`);
|
|
19
|
+
return "DATABASE_DOES_NOT_EXIST";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
15
22
|
return undefined;
|
|
16
23
|
}
|
|
17
24
|
}
|
|
@@ -15,7 +15,7 @@ exports.support = "preview";
|
|
|
15
15
|
exports.type = 3;
|
|
16
16
|
exports.docsUrl = "https://firebase.google.com/docs/hosting/frameworks/angular";
|
|
17
17
|
const DEFAULT_BUILD_SCRIPT = ["ng build"];
|
|
18
|
-
exports.supportedRange = "
|
|
18
|
+
exports.supportedRange = "16 - 18";
|
|
19
19
|
async function discover(dir) {
|
|
20
20
|
if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "package.json"))))
|
|
21
21
|
return;
|
|
@@ -181,9 +181,21 @@ exports.handle = function(req,res) {
|
|
|
181
181
|
};\n`;
|
|
182
182
|
}
|
|
183
183
|
else if (serverOutputPath) {
|
|
184
|
-
bootstrapScript = `
|
|
184
|
+
bootstrapScript = `
|
|
185
|
+
const app = new Promise((resolve) => {
|
|
186
|
+
setTimeout(() => {
|
|
187
|
+
const port = process.env.PORT;
|
|
188
|
+
const socket = 'express.sock';
|
|
189
|
+
process.env.PORT = socket;
|
|
190
|
+
|
|
191
|
+
${(serverEntry === null || serverEntry === void 0 ? void 0 : serverEntry.endsWith(".mjs"))
|
|
185
192
|
? `import(\`./${serverOutputPath}/${serverEntry}\`)`
|
|
186
|
-
: `Promise.resolve(require('./${serverOutputPath}/${serverEntry}'))`}.then(
|
|
193
|
+
: `Promise.resolve(require('./${serverOutputPath}/${serverEntry}'))`}.then(({ app }) => {
|
|
194
|
+
process.env.PORT = port;
|
|
195
|
+
resolve(app());
|
|
196
|
+
});
|
|
197
|
+
}, 0);
|
|
198
|
+
});
|
|
187
199
|
exports.handle = (req,res) => app.then(it => it(req,res));\n`;
|
|
188
200
|
}
|
|
189
201
|
else {
|
|
@@ -265,7 +265,7 @@ exports.findEsbuildPath = findEsbuildPath;
|
|
|
265
265
|
function getGlobalEsbuildVersion(binPath) {
|
|
266
266
|
var _a;
|
|
267
267
|
try {
|
|
268
|
-
const versionOutput = (_a = (0, child_process_1.execSync)(
|
|
268
|
+
const versionOutput = (_a = (0, child_process_1.execSync)(`"${binPath}" --version`, { encoding: "utf8" })) === null || _a === void 0 ? void 0 : _a.trim();
|
|
269
269
|
if (!versionOutput) {
|
|
270
270
|
return null;
|
|
271
271
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setupSQLPermissions = exports.iamUserIsCSQLAdmin = exports.checkSQLRoleIsGranted = exports.firebasewriter = exports.firebasereader = exports.firebaseowner = void 0;
|
|
3
|
+
exports.setupSQLPermissions = exports.iamUserIsCSQLAdmin = exports.checkSQLRoleIsGranted = exports.fdcSqlRoleMap = exports.firebasewriter = exports.firebasereader = exports.firebaseowner = void 0;
|
|
4
4
|
const projectUtils_1 = require("../../projectUtils");
|
|
5
5
|
const connect_1 = require("./connect");
|
|
6
6
|
const iam_1 = require("../iam");
|
|
@@ -19,6 +19,11 @@ function firebasewriter(databaseId) {
|
|
|
19
19
|
return `firebasewriter_${databaseId}_public`;
|
|
20
20
|
}
|
|
21
21
|
exports.firebasewriter = firebasewriter;
|
|
22
|
+
exports.fdcSqlRoleMap = {
|
|
23
|
+
owner: firebaseowner,
|
|
24
|
+
writer: firebasewriter,
|
|
25
|
+
reader: firebasereader,
|
|
26
|
+
};
|
|
22
27
|
async function checkSQLRoleIsGranted(options, instanceId, databaseId, grantedRole, granteeRole) {
|
|
23
28
|
const checkCmd = `
|
|
24
29
|
DO $$
|
package/lib/gcp/secretManager.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.labels = exports.ensureApi = exports.isAppHostingManaged = exports.isFunctionsManaged = exports.FIREBASE_MANAGED = exports.ensureServiceAgentRole = exports.setIamPolicy = exports.getIamPolicy = exports.addVersion = exports.deleteSecret = exports.patchSecret = exports.createSecret = exports.toSecretVersionResourceName = exports.parseSecretVersionResourceName = exports.parseSecretResourceName = exports.secretExists = exports.destroySecretVersion = exports.accessSecretVersion = exports.getSecretVersion = exports.listSecretVersions = exports.getSecretMetadata = exports.listSecrets = exports.getSecret = exports.secretManagerConsoleUri = void 0;
|
|
3
|
+
exports.labels = exports.ensureApi = exports.isAppHostingManaged = exports.isFunctionsManaged = exports.FIREBASE_MANAGED = exports.checkServiceAgentRole = exports.ensureServiceAgentRole = exports.setIamPolicy = exports.getIamPolicy = exports.addVersion = exports.deleteSecret = exports.patchSecret = exports.createSecret = exports.toSecretVersionResourceName = exports.parseSecretVersionResourceName = exports.parseSecretResourceName = exports.secretExists = exports.destroySecretVersion = exports.accessSecretVersion = exports.getSecretVersion = exports.listSecretVersions = exports.getSecretMetadata = exports.listSecrets = exports.getSecret = exports.secretManagerConsoleUri = void 0;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const error_1 = require("../error");
|
|
6
6
|
const apiv2_1 = require("../apiv2");
|
|
@@ -207,6 +207,14 @@ async function setIamPolicy(secret, bindings) {
|
|
|
207
207
|
}
|
|
208
208
|
exports.setIamPolicy = setIamPolicy;
|
|
209
209
|
async function ensureServiceAgentRole(secret, serviceAccountEmails, role) {
|
|
210
|
+
const bindings = await checkServiceAgentRole(secret, serviceAccountEmails, role);
|
|
211
|
+
if (bindings.length) {
|
|
212
|
+
await module.exports.setIamPolicy(secret, bindings);
|
|
213
|
+
}
|
|
214
|
+
(0, utils_1.logLabeledSuccess)("secretmanager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmails.join(", ")}`);
|
|
215
|
+
}
|
|
216
|
+
exports.ensureServiceAgentRole = ensureServiceAgentRole;
|
|
217
|
+
async function checkServiceAgentRole(secret, serviceAccountEmails, role) {
|
|
210
218
|
const policy = await module.exports.getIamPolicy(secret);
|
|
211
219
|
const bindings = policy.bindings || [];
|
|
212
220
|
let binding = bindings.find((b) => b.role === role);
|
|
@@ -222,11 +230,10 @@ async function ensureServiceAgentRole(secret, serviceAccountEmails, role) {
|
|
|
222
230
|
}
|
|
223
231
|
}
|
|
224
232
|
if (shouldShortCircuit)
|
|
225
|
-
return;
|
|
226
|
-
|
|
227
|
-
(0, utils_1.logLabeledSuccess)("secretmanager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmails.join(", ")}`);
|
|
233
|
+
return [];
|
|
234
|
+
return bindings;
|
|
228
235
|
}
|
|
229
|
-
exports.
|
|
236
|
+
exports.checkServiceAgentRole = checkServiceAgentRole;
|
|
230
237
|
exports.FIREBASE_MANAGED = "firebase-managed";
|
|
231
238
|
function isFunctionsManaged(secret) {
|
|
232
239
|
return (secret.labels[exports.FIREBASE_MANAGED] === "true" || secret.labels[exports.FIREBASE_MANAGED] === "functions");
|
|
@@ -8,13 +8,16 @@ const provisionCloudSql_1 = require("../../../dataconnect/provisionCloudSql");
|
|
|
8
8
|
const freeTrial_1 = require("../../../dataconnect/freeTrial");
|
|
9
9
|
const cloudsql = require("../../../gcp/cloudsql/cloudsqladmin");
|
|
10
10
|
const ensureApis_1 = require("../../../dataconnect/ensureApis");
|
|
11
|
+
const experiments = require("../../../experiments");
|
|
11
12
|
const client_1 = require("../../../dataconnect/client");
|
|
12
13
|
const emulators_1 = require("../emulators");
|
|
13
14
|
const names_1 = require("../../../dataconnect/names");
|
|
14
15
|
const logger_1 = require("../../../logger");
|
|
15
16
|
const templates_1 = require("../../../templates");
|
|
16
17
|
const utils_1 = require("../../../utils");
|
|
18
|
+
const cloudbilling_1 = require("../../../gcp/cloudbilling");
|
|
17
19
|
const DATACONNECT_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/dataconnect.yaml");
|
|
20
|
+
const DATACONNECT_YAML_COMPAT_EXPERIMENT_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/dataconnect-fdccompatiblemode.yaml");
|
|
18
21
|
const CONNECTOR_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/connector.yaml");
|
|
19
22
|
const SCHEMA_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/schema.gql");
|
|
20
23
|
const QUERIES_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/queries.gql");
|
|
@@ -53,7 +56,8 @@ async function askQuestions(setup, config) {
|
|
|
53
56
|
schemaGql: [],
|
|
54
57
|
shouldProvisionCSQL: false,
|
|
55
58
|
};
|
|
56
|
-
|
|
59
|
+
const isBillingEnabled = setup.projectId ? await (0, cloudbilling_1.checkBillingEnabled)(setup.projectId) : false;
|
|
60
|
+
info = await promptForService(setup, info, isBillingEnabled);
|
|
57
61
|
if (info.cloudSqlInstanceId === "") {
|
|
58
62
|
info = await promptForCloudSQLInstance(setup, info);
|
|
59
63
|
}
|
|
@@ -70,8 +74,9 @@ async function askQuestions(setup, config) {
|
|
|
70
74
|
setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
|
|
71
75
|
info.shouldProvisionCSQL = !!(setup.projectId &&
|
|
72
76
|
(info.isNewInstance || info.isNewDatabase) &&
|
|
77
|
+
isBillingEnabled &&
|
|
73
78
|
(await (0, prompt_1.confirm)({
|
|
74
|
-
message:
|
|
79
|
+
message: `Would you like to provision your Cloud SQL instance and database now?${info.isNewInstance ? " This will take several minutes." : ""}.`,
|
|
75
80
|
default: true,
|
|
76
81
|
})));
|
|
77
82
|
return info;
|
|
@@ -125,7 +130,9 @@ function subDataconnectYamlValues(replacementValues) {
|
|
|
125
130
|
connectorDirs: "__connectorDirs__",
|
|
126
131
|
locationId: "__location__",
|
|
127
132
|
};
|
|
128
|
-
let replaced =
|
|
133
|
+
let replaced = experiments.isEnabled("fdccompatiblemode")
|
|
134
|
+
? DATACONNECT_YAML_COMPAT_EXPERIMENT_TEMPLATE
|
|
135
|
+
: DATACONNECT_YAML_TEMPLATE;
|
|
129
136
|
for (const [k, v] of Object.entries(replacementValues)) {
|
|
130
137
|
replaced = replaced.replace(replacements[k], JSON.stringify(v));
|
|
131
138
|
}
|
|
@@ -141,67 +148,73 @@ function subConnectorYamlValues(replacementValues) {
|
|
|
141
148
|
}
|
|
142
149
|
return replaced;
|
|
143
150
|
}
|
|
144
|
-
async function promptForService(setup, info) {
|
|
145
|
-
var _a, _b, _c
|
|
151
|
+
async function promptForService(setup, info, isBillingEnabled) {
|
|
152
|
+
var _a, _b, _c;
|
|
146
153
|
if (setup.projectId) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
service: s,
|
|
152
|
-
schema: await (0, client_1.getSchema)(s.name),
|
|
153
|
-
};
|
|
154
|
-
}));
|
|
155
|
-
if (existingServicesAndSchemas.length) {
|
|
156
|
-
const choices = existingServicesAndSchemas.map((s) => {
|
|
157
|
-
const serviceName = (0, names_1.parseServiceName)(s.service.name);
|
|
154
|
+
if (isBillingEnabled) {
|
|
155
|
+
await (0, ensureApis_1.ensureApis)(setup.projectId);
|
|
156
|
+
const existingServices = await (0, client_1.listAllServices)(setup.projectId);
|
|
157
|
+
const existingServicesAndSchemas = await Promise.all(existingServices.map(async (s) => {
|
|
158
158
|
return {
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
service: s,
|
|
160
|
+
schema: await (0, client_1.getSchema)(s.name),
|
|
161
161
|
};
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
162
|
+
}));
|
|
163
|
+
if (existingServicesAndSchemas.length) {
|
|
164
|
+
const choices = existingServicesAndSchemas.map((s) => {
|
|
165
|
+
const serviceName = (0, names_1.parseServiceName)(s.service.name);
|
|
166
|
+
return {
|
|
167
|
+
name: `${serviceName.location}/${serviceName.serviceId}`,
|
|
168
|
+
value: s,
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
choices.push({ name: "Create a new service", value: undefined });
|
|
172
|
+
const choice = await (0, prompt_1.promptOnce)({
|
|
173
|
+
message: "Your project already has existing services. Which would you like to set up local files for?",
|
|
174
|
+
type: "list",
|
|
175
|
+
choices,
|
|
176
|
+
});
|
|
177
|
+
if (choice) {
|
|
178
|
+
const serviceName = (0, names_1.parseServiceName)(choice.service.name);
|
|
179
|
+
info.serviceId = serviceName.serviceId;
|
|
180
|
+
info.locationId = serviceName.location;
|
|
181
|
+
if (choice.schema) {
|
|
182
|
+
const primaryDatasource = choice.schema.datasources.find((d) => d.postgresql);
|
|
183
|
+
if ((_a = primaryDatasource === null || primaryDatasource === void 0 ? void 0 : primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) {
|
|
184
|
+
const instanceName = (0, names_1.parseCloudSQLInstanceName)(primaryDatasource.postgresql.cloudSql.instance);
|
|
185
|
+
info.cloudSqlInstanceId = instanceName.instanceId;
|
|
186
|
+
}
|
|
187
|
+
if (choice.schema.source.files) {
|
|
188
|
+
info.schemaGql = choice.schema.source.files;
|
|
189
|
+
}
|
|
190
|
+
info.cloudSqlDatabase = (_c = (_b = primaryDatasource === null || primaryDatasource === void 0 ? void 0 : primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.database) !== null && _c !== void 0 ? _c : "";
|
|
191
|
+
const connectors = await (0, client_1.listConnectors)(choice.service.name, [
|
|
192
|
+
"connectors.name",
|
|
193
|
+
"connectors.source.files",
|
|
194
|
+
]);
|
|
195
|
+
if (connectors.length) {
|
|
196
|
+
info.connectors = connectors.map((c) => {
|
|
197
|
+
const id = c.name.split("/").pop();
|
|
198
|
+
return {
|
|
199
|
+
id,
|
|
200
|
+
path: connectors.length === 1 ? "./connector" : `./${id}`,
|
|
201
|
+
files: c.source.files || [],
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
}
|
|
195
205
|
}
|
|
196
206
|
}
|
|
197
207
|
}
|
|
198
208
|
}
|
|
209
|
+
else {
|
|
210
|
+
await (0, ensureApis_1.ensureSparkApis)(setup.projectId);
|
|
211
|
+
}
|
|
199
212
|
}
|
|
200
213
|
if (info.serviceId === "") {
|
|
201
214
|
info.serviceId = await (0, prompt_1.promptOnce)({
|
|
202
215
|
message: "What ID would you like to use for this service?",
|
|
203
216
|
type: "input",
|
|
204
|
-
default: "
|
|
217
|
+
default: "app",
|
|
205
218
|
});
|
|
206
219
|
}
|
|
207
220
|
return info;
|
|
@@ -233,7 +246,7 @@ async function promptForCloudSQLInstance(setup, info) {
|
|
|
233
246
|
info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
234
247
|
message: `What ID would you like to use for your new CloudSQL instance?`,
|
|
235
248
|
type: "input",
|
|
236
|
-
default:
|
|
249
|
+
default: `${info.serviceId || "app"}-fdc`,
|
|
237
250
|
});
|
|
238
251
|
}
|
|
239
252
|
if (info.locationId === "") {
|
|
@@ -9,13 +9,14 @@ const rules = require("./rules");
|
|
|
9
9
|
const indexes = require("./indexes");
|
|
10
10
|
const error_1 = require("../../../error");
|
|
11
11
|
const clc = require("colorette");
|
|
12
|
+
const prompt_1 = require("../../../prompt");
|
|
12
13
|
async function checkProjectSetup(setup, config, options) {
|
|
13
14
|
const firestoreUnusedError = new error_1.FirebaseError(`It looks like you haven't used Cloud Firestore in this project before. Go to ${clc.bold(clc.underline(`https://console.firebase.google.com/project/${setup.projectId}/firestore`))} to create your Cloud Firestore database.`, { exit: 1 });
|
|
14
15
|
const isFirestoreEnabled = await apiEnabled.check(setup.projectId, "firestore.googleapis.com", "", true);
|
|
15
16
|
if (!isFirestoreEnabled) {
|
|
16
17
|
throw firestoreUnusedError;
|
|
17
18
|
}
|
|
18
|
-
const dbType = await (
|
|
19
|
+
const dbType = await getDatabaseType(setup);
|
|
19
20
|
logger_1.logger.debug(`database_type: ${dbType}`);
|
|
20
21
|
if (!dbType) {
|
|
21
22
|
throw firestoreUnusedError;
|
|
@@ -25,6 +26,24 @@ async function checkProjectSetup(setup, config, options) {
|
|
|
25
26
|
}
|
|
26
27
|
await (0, requirePermissions_1.requirePermissions)(Object.assign(Object.assign({}, options), { project: setup.projectId }));
|
|
27
28
|
}
|
|
29
|
+
async function getDatabaseType(setup) {
|
|
30
|
+
const dbType = await (0, checkDatabaseType_1.checkDatabaseType)(setup.projectId, setup.databaseId);
|
|
31
|
+
logger_1.logger.debug(`database_type: ${dbType}`);
|
|
32
|
+
if (dbType === "DATABASE_DOES_NOT_EXIST") {
|
|
33
|
+
setup.databaseId = await selectDatabaseByPrompting();
|
|
34
|
+
return await getDatabaseType(setup);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return dbType;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function selectDatabaseByPrompting() {
|
|
41
|
+
const database = await (0, prompt_1.promptOnce)({
|
|
42
|
+
type: "input",
|
|
43
|
+
message: "Please input the name of the Native Firestore database you would like to use:",
|
|
44
|
+
});
|
|
45
|
+
return database;
|
|
46
|
+
}
|
|
28
47
|
async function doSetup(setup, config, options) {
|
|
29
48
|
if (setup.projectId) {
|
|
30
49
|
await checkProjectSetup(setup, config, options);
|
|
@@ -47,15 +47,15 @@ function initIndexes(setup, config) {
|
|
|
47
47
|
if (!setup.projectId) {
|
|
48
48
|
return config.writeProjectFile(setup.config.firestore.indexes, INDEXES_TEMPLATE);
|
|
49
49
|
}
|
|
50
|
-
return getIndexesFromConsole(setup.projectId).then((contents) => {
|
|
50
|
+
return getIndexesFromConsole(setup.projectId, setup.databaseId).then((contents) => {
|
|
51
51
|
return config.writeProjectFile(setup.config.firestore.indexes, contents);
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
exports.initIndexes = initIndexes;
|
|
56
|
-
function getIndexesFromConsole(projectId) {
|
|
57
|
-
const indexesPromise = indexes.listIndexes(projectId);
|
|
58
|
-
const fieldOverridesPromise = indexes.listFieldOverrides(projectId);
|
|
56
|
+
function getIndexesFromConsole(projectId, databaseId) {
|
|
57
|
+
const indexesPromise = indexes.listIndexes(projectId, databaseId);
|
|
58
|
+
const fieldOverridesPromise = indexes.listFieldOverrides(projectId, databaseId);
|
|
59
59
|
return Promise.all([indexesPromise, fieldOverridesPromise])
|
|
60
60
|
.then((res) => {
|
|
61
61
|
return indexes.makeIndexSpec(res[0], res[1]);
|
package/package.json
CHANGED
|
@@ -43,6 +43,16 @@
|
|
|
43
43
|
"description": "Path to the directory where generated files should be written to."
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
+
},
|
|
47
|
+
"llmTools": {
|
|
48
|
+
"additionalProperties": true,
|
|
49
|
+
"type": "object",
|
|
50
|
+
"properties": {
|
|
51
|
+
"outputFile": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Path where the JSON LLM tool definitions file should be generated."
|
|
54
|
+
}
|
|
55
|
+
}
|
|
46
56
|
}
|
|
47
57
|
},
|
|
48
58
|
"properties": {
|
|
@@ -50,35 +60,51 @@
|
|
|
50
60
|
"type": "string",
|
|
51
61
|
"description": "The ID of the Firebase Data Connect connector."
|
|
52
62
|
},
|
|
53
|
-
"authMode": {
|
|
54
|
-
"type": "string",
|
|
55
|
-
"description": "The authentication strategy to use for this connector"
|
|
56
|
-
|
|
57
|
-
},
|
|
58
63
|
"generate": {
|
|
59
64
|
"type": "object",
|
|
60
65
|
"additionalProperties": false,
|
|
61
66
|
"properties": {
|
|
62
67
|
"javascriptSdk": {
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
"oneOf": [
|
|
69
|
+
{ "$ref": "#/definitions/javascriptSdk" },
|
|
70
|
+
{
|
|
71
|
+
"type": "array",
|
|
72
|
+
"items": {
|
|
73
|
+
"$ref": "#/definitions/javascriptSdk"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
],
|
|
67
77
|
"description": "Configuration for a generated Javascript SDK"
|
|
68
78
|
},
|
|
69
79
|
"kotlinSdk": {
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
"oneOf": [
|
|
81
|
+
{ "$ref": "#/definitions/kotlinSdk" },
|
|
82
|
+
{
|
|
83
|
+
"type": "array",
|
|
84
|
+
"items": {
|
|
85
|
+
"$ref": "#/definitions/kotlinSdk"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
],
|
|
74
89
|
"description": "Configuration for a generated Kotlin SDK"
|
|
75
90
|
},
|
|
76
91
|
"swiftSdk": {
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
92
|
+
"oneOf": [
|
|
93
|
+
{ "$ref": "#/definitions/swiftSdk" },
|
|
94
|
+
{
|
|
95
|
+
"type": "array",
|
|
96
|
+
"items": {
|
|
97
|
+
"$ref": "#/definitions/swiftSdk"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
],
|
|
81
101
|
"description": "Configuration for a generated Swift SDK"
|
|
102
|
+
},
|
|
103
|
+
"llmTools": {
|
|
104
|
+
"oneOf": [
|
|
105
|
+
{ "$ref": "#/definitions/llmTools" },
|
|
106
|
+
{ "type": "array", "items": { "$ref": "#/definitions/llmTools" } }
|
|
107
|
+
]
|
|
82
108
|
}
|
|
83
109
|
}
|
|
84
110
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
specVersion: "v1beta"
|
|
2
|
+
serviceId: __serviceId__
|
|
3
|
+
location: __location__
|
|
4
|
+
schema:
|
|
5
|
+
source: "./schema"
|
|
6
|
+
datasource:
|
|
7
|
+
postgresql:
|
|
8
|
+
database: __cloudSqlDatabase__
|
|
9
|
+
cloudSql:
|
|
10
|
+
instanceId: __cloudSqlInstanceId__
|
|
11
|
+
# schemaValidation: "COMPATIBLE"
|
|
12
|
+
connectorDirs: __connectorDirs__
|