firebase-tools 13.8.1 → 13.8.3
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/apphosting/app.js +9 -7
- package/lib/apphosting/githubConnections.js +6 -8
- package/lib/apphosting/index.js +20 -19
- package/lib/apphosting/repo.js +2 -2
- package/lib/commands/apphosting-backends-create.js +3 -3
- package/lib/commands/{dataconnect-list.js → dataconnect-services-list.js} +23 -6
- package/lib/commands/dataconnect-sql-migrate.js +3 -3
- package/lib/commands/index.js +2 -1
- package/lib/commands/init.js +3 -3
- package/lib/dataconnect/client.js +2 -2
- package/lib/dataconnect/errors.js +31 -0
- package/lib/dataconnect/fileUtils.js +10 -7
- package/lib/dataconnect/freeTrial.js +1 -1
- package/lib/dataconnect/graphqlError.js +2 -2
- package/lib/dataconnect/provisionCloudSql.js +4 -3
- package/lib/dataconnect/schemaMigration.js +119 -46
- package/lib/deploy/dataconnect/deploy.js +4 -14
- package/lib/deploy/dataconnect/prepare.js +3 -0
- package/lib/emulator/dataconnectEmulator.js +2 -1
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/experiments.js +5 -0
- package/lib/gcp/cloudsql/cloudsqladmin.js +39 -19
- package/lib/gcp/firedata.js +3 -2
- package/lib/init/features/dataconnect/index.js +39 -33
- package/lib/init/features/emulators.js +9 -10
- package/lib/init/features/functions/index.js +4 -0
- package/lib/init/features/functions/npm-dependencies.js +12 -23
- package/lib/init/features/genkit.js +54 -0
- package/lib/init/features/index.js +3 -1
- package/lib/init/index.js +1 -0
- package/lib/init/spawn.js +23 -0
- package/lib/requireTosAcceptance.js +1 -0
- package/package.json +1 -1
- package/templates/init/dataconnect/mutations.gql +29 -3
- package/templates/init/dataconnect/queries.gql +49 -4
- package/templates/init/dataconnect/schema.gql +23 -10
|
@@ -11,66 +11,104 @@ const logger_1 = require("../logger");
|
|
|
11
11
|
const error_1 = require("../error");
|
|
12
12
|
const projectUtils_1 = require("../projectUtils");
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
|
-
const
|
|
14
|
+
const errors = require("./errors");
|
|
15
15
|
async function diffSchema(schema) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const instanceName = (_b = schema.primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.cloudSql.instance;
|
|
19
|
-
if (!instanceName || !dbName) {
|
|
20
|
-
throw new error_1.FirebaseError(`tried to diff schema but ${instanceName} was undefined`);
|
|
21
|
-
}
|
|
16
|
+
const { serviceName, instanceName, databaseId } = getIdentifiers(schema);
|
|
17
|
+
await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId);
|
|
22
18
|
try {
|
|
23
|
-
const serviceName = schema.name.replace(`/schemas/${types_1.SCHEMA_ID}`, "");
|
|
24
|
-
await ensureServiceIsConnectedToCloudSql(serviceName);
|
|
25
19
|
await (0, client_1.upsertSchema)(schema, true);
|
|
26
20
|
}
|
|
27
21
|
catch (err) {
|
|
28
|
-
const
|
|
22
|
+
const invalidConnectors = errors.getInvalidConnectors(err);
|
|
23
|
+
if (invalidConnectors.length) {
|
|
24
|
+
displayInvalidConnectors(invalidConnectors);
|
|
25
|
+
}
|
|
26
|
+
const incompatible = errors.getIncompatibleSchemaError(err);
|
|
29
27
|
if (incompatible) {
|
|
30
28
|
displaySchemaChanges(incompatible);
|
|
31
29
|
return incompatible.diffs;
|
|
32
30
|
}
|
|
33
|
-
throw err;
|
|
34
31
|
}
|
|
35
|
-
|
|
32
|
+
(0, utils_1.logLabeledSuccess)("dataconnect", `Database schema is up to date.`);
|
|
36
33
|
return [];
|
|
37
34
|
}
|
|
38
35
|
exports.diffSchema = diffSchema;
|
|
39
36
|
async function migrateSchema(args) {
|
|
40
|
-
|
|
41
|
-
const {
|
|
42
|
-
|
|
43
|
-
if (!databaseId) {
|
|
44
|
-
throw new error_1.FirebaseError("Schema is missing primaryDatasource.postgresql?.database, cannot migrate");
|
|
45
|
-
}
|
|
46
|
-
const instanceId = (_b = schema.primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.cloudSql.instance.split("/").pop();
|
|
47
|
-
if (!instanceId) {
|
|
48
|
-
throw new error_1.FirebaseError(`tried to migrate schema but ${instanceId} was undefined`);
|
|
49
|
-
}
|
|
37
|
+
const { options, schema, allowNonInteractiveMigration, validateOnly } = args;
|
|
38
|
+
const { serviceName, instanceId, instanceName, databaseId } = getIdentifiers(schema);
|
|
39
|
+
await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId);
|
|
50
40
|
try {
|
|
51
|
-
const serviceName = schema.name.replace(`/schemas/${types_1.SCHEMA_ID}`, "");
|
|
52
|
-
await ensureServiceIsConnectedToCloudSql(serviceName);
|
|
53
41
|
await (0, client_1.upsertSchema)(schema, validateOnly);
|
|
54
42
|
logger_1.logger.debug(`Database schema was up to date for ${instanceId}:${databaseId}`);
|
|
55
|
-
return [];
|
|
56
43
|
}
|
|
57
44
|
catch (err) {
|
|
58
|
-
const incompatible = getIncompatibleSchemaError(err);
|
|
59
|
-
|
|
45
|
+
const incompatible = errors.getIncompatibleSchemaError(err);
|
|
46
|
+
const invalidConnectors = errors.getInvalidConnectors(err);
|
|
47
|
+
if (!incompatible && !invalidConnectors.length) {
|
|
60
48
|
throw err;
|
|
61
49
|
}
|
|
62
|
-
const
|
|
63
|
-
|
|
50
|
+
const shouldDeleteInvalidConnectors = await promptForInvalidConnectorError(options, invalidConnectors, validateOnly);
|
|
51
|
+
if (!shouldDeleteInvalidConnectors && invalidConnectors.length) {
|
|
52
|
+
const cmd = suggestedCommand(serviceName, invalidConnectors);
|
|
53
|
+
throw new error_1.FirebaseError(`Command aborted. Try deploying compatible connectors first with ${clc.bold(cmd)}`);
|
|
54
|
+
}
|
|
55
|
+
const migrationMode = incompatible
|
|
56
|
+
? await promptForSchemaMigration(options, databaseId, incompatible, allowNonInteractiveMigration)
|
|
57
|
+
: "none";
|
|
58
|
+
if (migrationMode === "none" && incompatible) {
|
|
59
|
+
throw new error_1.FirebaseError("Command aborted.");
|
|
60
|
+
}
|
|
61
|
+
let diffs = [];
|
|
62
|
+
if (incompatible) {
|
|
63
|
+
diffs = await handleIncompatibleSchemaError({
|
|
64
|
+
options,
|
|
65
|
+
databaseId,
|
|
66
|
+
instanceId,
|
|
67
|
+
incompatibleSchemaError: incompatible,
|
|
68
|
+
choice: migrationMode,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (invalidConnectors.length) {
|
|
72
|
+
await deleteInvalidConnectors(invalidConnectors);
|
|
73
|
+
}
|
|
64
74
|
await (0, client_1.upsertSchema)(schema, validateOnly);
|
|
65
75
|
return diffs;
|
|
66
76
|
}
|
|
77
|
+
return [];
|
|
67
78
|
}
|
|
68
79
|
exports.migrateSchema = migrateSchema;
|
|
80
|
+
function getIdentifiers(schema) {
|
|
81
|
+
var _a, _b;
|
|
82
|
+
const databaseId = (_a = schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.database;
|
|
83
|
+
if (!databaseId) {
|
|
84
|
+
throw new error_1.FirebaseError("Schema is missing primaryDatasource.postgresql?.database, cannot migrate");
|
|
85
|
+
}
|
|
86
|
+
const instanceName = (_b = schema.primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.cloudSql.instance;
|
|
87
|
+
if (!instanceName) {
|
|
88
|
+
throw new error_1.FirebaseError("tried to migrate schema but instance name was not provided in dataconnect.yaml");
|
|
89
|
+
}
|
|
90
|
+
const instanceId = instanceName.split("/").pop();
|
|
91
|
+
const serviceName = schema.name.replace(`/schemas/${types_1.SCHEMA_ID}`, "");
|
|
92
|
+
return {
|
|
93
|
+
databaseId,
|
|
94
|
+
instanceId,
|
|
95
|
+
instanceName,
|
|
96
|
+
serviceName,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function suggestedCommand(serviceName, invalidConnectorNames) {
|
|
100
|
+
const serviceId = serviceName.split("/")[5];
|
|
101
|
+
const connectorIds = invalidConnectorNames.map((i) => i.split("/")[7]);
|
|
102
|
+
const onlys = connectorIds.map((c) => `dataconnect:${serviceId}:${c}`).join(",");
|
|
103
|
+
return `firebase deploy --only ${onlys}`;
|
|
104
|
+
}
|
|
69
105
|
async function handleIncompatibleSchemaError(args) {
|
|
70
|
-
const { incompatibleSchemaError, options, instanceId, databaseId,
|
|
106
|
+
const { incompatibleSchemaError, options, instanceId, databaseId, choice } = args;
|
|
107
|
+
if (incompatibleSchemaError.destructive && choice === "safe") {
|
|
108
|
+
throw new error_1.FirebaseError("This schema migration includes potentially destructive changes. If you'd like to execute it anyway, rerun this command with --force");
|
|
109
|
+
}
|
|
71
110
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
72
111
|
const iamUser = await (0, connect_1.setupIAMUser)(instanceId, databaseId, options);
|
|
73
|
-
const choice = await promptForSchemaMigration(options, databaseId, incompatibleSchemaError, allowNonInteractiveMigration);
|
|
74
112
|
const commandsToExecute = incompatibleSchemaError.diffs
|
|
75
113
|
.filter((d) => {
|
|
76
114
|
switch (choice) {
|
|
@@ -104,7 +142,6 @@ async function promptForSchemaMigration(options, databaseName, err, allowNonInte
|
|
|
104
142
|
const choices = err.destructive
|
|
105
143
|
? [
|
|
106
144
|
{ name: "Execute all changes (including destructive changes)", value: "all" },
|
|
107
|
-
{ name: "Execute only safe changes", value: "safe" },
|
|
108
145
|
{ name: "Abort changes", value: "none" },
|
|
109
146
|
]
|
|
110
147
|
: [
|
|
@@ -132,23 +169,69 @@ async function promptForSchemaMigration(options, databaseName, err, allowNonInte
|
|
|
132
169
|
return "none";
|
|
133
170
|
}
|
|
134
171
|
}
|
|
135
|
-
async function
|
|
172
|
+
async function promptForInvalidConnectorError(options, invalidConnectors, validateOnly) {
|
|
173
|
+
if (!invalidConnectors.length) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
displayInvalidConnectors(invalidConnectors);
|
|
177
|
+
if (validateOnly) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
else if (options.force ||
|
|
181
|
+
(!options.nonInteractive &&
|
|
182
|
+
(await (0, prompt_1.confirm)(Object.assign(Object.assign({}, options), { message: "Would you like to delete and recreate these connectors?" }))))) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
async function deleteInvalidConnectors(invalidConnectors) {
|
|
188
|
+
return Promise.all(invalidConnectors.map(client_1.deleteConnector));
|
|
189
|
+
}
|
|
190
|
+
function displayInvalidConnectors(invalidConnectors) {
|
|
191
|
+
const connectorIds = invalidConnectors.map((i) => i.split("/").pop()).join(", ");
|
|
192
|
+
(0, utils_1.logLabeledWarning)("dataconnect", `The schema you are deploying is incompatible with the following existing connectors: ${connectorIds}.`);
|
|
193
|
+
(0, utils_1.logLabeledWarning)("dataconnect", `This is a ${clc.red("breaking")} change and will cause a brief downtime.`);
|
|
194
|
+
}
|
|
195
|
+
async function ensureServiceIsConnectedToCloudSql(serviceName, instanceId, databaseId) {
|
|
136
196
|
let currentSchema;
|
|
137
197
|
try {
|
|
138
198
|
currentSchema = await (0, client_1.getSchema)(serviceName);
|
|
139
199
|
}
|
|
140
200
|
catch (err) {
|
|
141
201
|
if (err.status === 404) {
|
|
142
|
-
|
|
202
|
+
currentSchema = {
|
|
203
|
+
name: `${serviceName}/schemas/${types_1.SCHEMA_ID}`,
|
|
204
|
+
source: {
|
|
205
|
+
files: [],
|
|
206
|
+
},
|
|
207
|
+
primaryDatasource: {
|
|
208
|
+
postgresql: {
|
|
209
|
+
database: databaseId,
|
|
210
|
+
cloudSql: {
|
|
211
|
+
instance: instanceId,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
throw err;
|
|
143
219
|
}
|
|
144
|
-
throw err;
|
|
145
220
|
}
|
|
146
221
|
if (!currentSchema.primaryDatasource.postgresql ||
|
|
147
222
|
currentSchema.primaryDatasource.postgresql.schemaValidation === "STRICT") {
|
|
148
223
|
return;
|
|
149
224
|
}
|
|
150
225
|
currentSchema.primaryDatasource.postgresql.schemaValidation = "STRICT";
|
|
151
|
-
|
|
226
|
+
try {
|
|
227
|
+
await (0, client_1.upsertSchema)(currentSchema, false);
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
if (err.status >= 500) {
|
|
231
|
+
throw err;
|
|
232
|
+
}
|
|
233
|
+
logger_1.logger.debug(err);
|
|
234
|
+
}
|
|
152
235
|
}
|
|
153
236
|
function displaySchemaChanges(error) {
|
|
154
237
|
const message = "Your new schema is incompatible with the schema of your CloudSQL database. " +
|
|
@@ -159,13 +242,3 @@ function displaySchemaChanges(error) {
|
|
|
159
242
|
function toString(diff) {
|
|
160
243
|
return `\/** ${diff.destructive ? clc.red("Destructive: ") : ""}${diff.description}*\/\n${(0, sql_formatter_1.format)(diff.sql, { language: "postgresql" })}`;
|
|
161
244
|
}
|
|
162
|
-
function getIncompatibleSchemaError(err) {
|
|
163
|
-
var _a;
|
|
164
|
-
const original = ((_a = err.context) === null || _a === void 0 ? void 0 : _a.body.error) || err.orignal;
|
|
165
|
-
if (!original) {
|
|
166
|
-
throw err;
|
|
167
|
-
}
|
|
168
|
-
const details = original.details;
|
|
169
|
-
const incompatibles = details.filter((d) => d["@type"] === IMCOMPATIBLE_SCHEMA_ERROR_TYPESTRING);
|
|
170
|
-
return incompatibles[0];
|
|
171
|
-
}
|
|
@@ -6,7 +6,6 @@ const types_1 = require("../../dataconnect/types");
|
|
|
6
6
|
const projectUtils_1 = require("../../projectUtils");
|
|
7
7
|
const provisionCloudSql_1 = require("../../dataconnect/provisionCloudSql");
|
|
8
8
|
const names_1 = require("../../dataconnect/names");
|
|
9
|
-
const prompt_1 = require("../../prompt");
|
|
10
9
|
const api_1 = require("../../api");
|
|
11
10
|
const ensureApiEnabled = require("../../ensureApiEnabled");
|
|
12
11
|
async function default_1(context, options) {
|
|
@@ -33,19 +32,10 @@ async function default_1(context, options) {
|
|
|
33
32
|
utils.logLabeledSuccess("dataconnect", `Created service ${s.serviceName}`);
|
|
34
33
|
}));
|
|
35
34
|
if (servicesToDelete.length) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.map((s) => s.name)
|
|
41
|
-
.join("\n")}\nWould you like to delete these services?`,
|
|
42
|
-
})) {
|
|
43
|
-
await Promise.all(servicesToDelete.map(async (s) => {
|
|
44
|
-
const { projectId, locationId, serviceId } = splitName(s.name);
|
|
45
|
-
await client.deleteService(projectId, locationId, serviceId);
|
|
46
|
-
utils.logLabeledSuccess("dataconnect", `Deleted service ${s.name}`);
|
|
47
|
-
}));
|
|
48
|
-
}
|
|
35
|
+
const warning = `The following services exist on ${projectId} but are not listed in your 'firebase.json'\n${servicesToDelete
|
|
36
|
+
.map((s) => s.name)
|
|
37
|
+
.join("\n")}\nConsider deleting these via the Firebase console if they are no longer needed.`;
|
|
38
|
+
utils.logLabeledWarning("dataconnect", warning);
|
|
49
39
|
}
|
|
50
40
|
utils.logLabeledBullet("dataconnect", "Checking for CloudSQL resources...");
|
|
51
41
|
await Promise.all(serviceInfos
|
|
@@ -9,9 +9,12 @@ const projectUtils_1 = require("../../projectUtils");
|
|
|
9
9
|
const filters_1 = require("../../dataconnect/filters");
|
|
10
10
|
const build_1 = require("../../dataconnect/build");
|
|
11
11
|
const ensureApis_1 = require("../../dataconnect/ensureApis");
|
|
12
|
+
const requireTosAcceptance_1 = require("../../requireTosAcceptance");
|
|
13
|
+
const firedata_1 = require("../../gcp/firedata");
|
|
12
14
|
async function default_1(context, options) {
|
|
13
15
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
14
16
|
await (0, ensureApis_1.ensureApis)(projectId);
|
|
17
|
+
await (0, requireTosAcceptance_1.requireTosAcceptance)(firedata_1.DATA_CONNECT_TOS_ID)(options);
|
|
15
18
|
const serviceCfgs = (0, fileUtils_1.readFirebaseJson)(options.config);
|
|
16
19
|
utils.logLabeledBullet("dataconnect", `Preparing to deploy`);
|
|
17
20
|
const filters = (0, filters_1.getResourceFilters)(options);
|
|
@@ -9,6 +9,7 @@ const types_1 = require("./types");
|
|
|
9
9
|
const error_1 = require("../error");
|
|
10
10
|
const emulatorLogger_1 = require("./emulatorLogger");
|
|
11
11
|
const types_2 = require("../dataconnect/types");
|
|
12
|
+
const grpcDefaultPort = 9510;
|
|
12
13
|
class DataConnectEmulator {
|
|
13
14
|
constructor(args) {
|
|
14
15
|
this.args = args;
|
|
@@ -26,7 +27,7 @@ class DataConnectEmulator {
|
|
|
26
27
|
this.logger.logLabeled("WARN", "Data Connect", "Operations that use vector_embed will make calls to production Vertex AI");
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
return (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, Object.assign(Object.assign({}, this.args), { http_port: port, grpc_port:
|
|
30
|
+
return (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, Object.assign(Object.assign({}, this.args), { http_port: port, grpc_port: grpcDefaultPort, config_dir: this.args.configDir, local_connection_string: this.getLocalConectionString(), project_id: this.args.projectId, service_location: this.args.locationId }));
|
|
30
31
|
}
|
|
31
32
|
connect() {
|
|
32
33
|
return Promise.resolve();
|
|
@@ -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.6",
|
|
27
|
+
expectedSize: 66349770,
|
|
28
|
+
expectedChecksum: "2eaabbe3cdb4867df585b7ec5505bad7",
|
|
29
29
|
},
|
|
30
30
|
storage: {
|
|
31
31
|
version: "1.1.3",
|
|
@@ -46,14 +46,14 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
46
46
|
},
|
|
47
47
|
dataconnect: process.platform === "darwin"
|
|
48
48
|
? {
|
|
49
|
-
version: "1.1.
|
|
50
|
-
expectedSize:
|
|
51
|
-
expectedChecksum: "
|
|
49
|
+
version: "1.1.17",
|
|
50
|
+
expectedSize: 25602224,
|
|
51
|
+
expectedChecksum: "1f9e3dd040a0ac4d1cb4d9dde4a3c0b0",
|
|
52
52
|
}
|
|
53
53
|
: {
|
|
54
|
-
version: "1.1.
|
|
55
|
-
expectedSize:
|
|
56
|
-
expectedChecksum: "
|
|
54
|
+
version: "1.1.17",
|
|
55
|
+
expectedSize: 23036912,
|
|
56
|
+
expectedChecksum: "a0ec0517108f842ed06fea14fe7c7e56",
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
exports.DownloadDetails = {
|
package/lib/experiments.js
CHANGED
|
@@ -96,6 +96,11 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
96
96
|
fullDescription: "Enable Data Connect related features.",
|
|
97
97
|
public: false,
|
|
98
98
|
},
|
|
99
|
+
genkit: {
|
|
100
|
+
shortDescription: "Enable Genkit related features.",
|
|
101
|
+
fullDescription: "Enable Genkit related features.",
|
|
102
|
+
public: false,
|
|
103
|
+
},
|
|
99
104
|
});
|
|
100
105
|
function isValidExperiment(name) {
|
|
101
106
|
return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
|
|
@@ -113,27 +113,47 @@ async function createDatabase(projectId, instanceId, databaseId) {
|
|
|
113
113
|
}
|
|
114
114
|
exports.createDatabase = createDatabase;
|
|
115
115
|
async function createUser(projectId, instanceId, type, username, password) {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
116
|
+
const maxRetries = 3;
|
|
117
|
+
let retries = 0;
|
|
118
|
+
while (true) {
|
|
119
|
+
try {
|
|
120
|
+
const op = await client.post(`projects/${projectId}/instances/${instanceId}/users`, {
|
|
121
|
+
name: username,
|
|
122
|
+
instance: instanceId,
|
|
123
|
+
project: projectId,
|
|
124
|
+
password: password,
|
|
125
|
+
sqlserverUserDetails: {
|
|
126
|
+
disabled: false,
|
|
127
|
+
serverRoles: ["cloudsqlsuperuser"],
|
|
128
|
+
},
|
|
129
|
+
type,
|
|
130
|
+
});
|
|
131
|
+
const opName = `projects/${projectId}/operations/${op.body.name}`;
|
|
132
|
+
const pollRes = await operationPoller.pollOperation({
|
|
133
|
+
apiOrigin: (0, api_1.cloudSQLAdminOrigin)(),
|
|
134
|
+
apiVersion: API_VERSION,
|
|
135
|
+
operationResourceName: opName,
|
|
136
|
+
doneFn: (op) => op.status === "DONE",
|
|
137
|
+
});
|
|
138
|
+
return pollRes;
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
if (builtinRoleNotReady(err.message) && retries < maxRetries) {
|
|
142
|
+
retries++;
|
|
143
|
+
await new Promise((resolve) => {
|
|
144
|
+
setTimeout(resolve, 1000 * retries);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
135
152
|
}
|
|
136
153
|
exports.createUser = createUser;
|
|
154
|
+
function builtinRoleNotReady(message) {
|
|
155
|
+
return message.includes("cloudsqliamuser");
|
|
156
|
+
}
|
|
137
157
|
async function getUser(projectId, instanceId, username) {
|
|
138
158
|
const res = await client.get(`projects/${projectId}/instances/${instanceId}/users/${username}`);
|
|
139
159
|
return res.body;
|
package/lib/gcp/firedata.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isProductTosAccepted = exports.getAcceptanceStatus = exports.getTosStatus = exports.APP_CHECK_TOS_ID = exports.APPHOSTING_TOS_ID = void 0;
|
|
3
|
+
exports.isProductTosAccepted = exports.getAcceptanceStatus = exports.getTosStatus = exports.DATA_CONNECT_TOS_ID = exports.APP_CHECK_TOS_ID = exports.APPHOSTING_TOS_ID = void 0;
|
|
4
4
|
const apiv2_1 = require("../apiv2");
|
|
5
5
|
const api_1 = require("../api");
|
|
6
6
|
const error_1 = require("../error");
|
|
7
7
|
const client = new apiv2_1.Client({ urlPrefix: (0, api_1.firedataOrigin)(), auth: true, apiVersion: "v1" });
|
|
8
8
|
exports.APPHOSTING_TOS_ID = "APP_HOSTING_TOS";
|
|
9
9
|
exports.APP_CHECK_TOS_ID = "APP_CHECK";
|
|
10
|
+
exports.DATA_CONNECT_TOS_ID = "FIREBASE_DATA_CONNECT";
|
|
10
11
|
async function getTosStatus() {
|
|
11
12
|
const res = await client.get("accessmanagement/tos:getStatus");
|
|
12
13
|
return res.body;
|
|
@@ -15,7 +16,7 @@ exports.getTosStatus = getTosStatus;
|
|
|
15
16
|
function getAcceptanceStatus(response, tosId) {
|
|
16
17
|
const perServiceStatus = response.perServiceStatus.find((tosStatus) => tosStatus.tosId === tosId);
|
|
17
18
|
if (perServiceStatus === undefined) {
|
|
18
|
-
throw new error_1.FirebaseError(`Missing terms of service status for
|
|
19
|
+
throw new error_1.FirebaseError(`Missing terms of service status for product: ${tosId}`);
|
|
19
20
|
}
|
|
20
21
|
return perServiceStatus.serviceStatus.status;
|
|
21
22
|
}
|
|
@@ -5,9 +5,11 @@ const path_1 = require("path");
|
|
|
5
5
|
const prompt_1 = require("../../../prompt");
|
|
6
6
|
const fs_1 = require("fs");
|
|
7
7
|
const provisionCloudSql_1 = require("../../../dataconnect/provisionCloudSql");
|
|
8
|
+
const freeTrial_1 = require("../../../dataconnect/freeTrial");
|
|
8
9
|
const cloudsql = require("../../../gcp/cloudsql/cloudsqladmin");
|
|
9
10
|
const ensureApis_1 = require("../../../dataconnect/ensureApis");
|
|
10
11
|
const client_1 = require("../../../dataconnect/client");
|
|
12
|
+
const emulators_1 = require("../emulators");
|
|
11
13
|
const TEMPLATE_ROOT = (0, path_1.resolve)(__dirname, "../../../../templates/init/dataconnect/");
|
|
12
14
|
const DATACONNECT_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "dataconnect.yaml"), "utf8");
|
|
13
15
|
const CONNECTOR_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "connector.yaml"), "utf8");
|
|
@@ -24,61 +26,65 @@ async function doSetup(setup, config) {
|
|
|
24
26
|
type: "input",
|
|
25
27
|
default: "dataconnect",
|
|
26
28
|
});
|
|
27
|
-
let locationOptions = [
|
|
28
|
-
{ name: "us-central1", value: "us-central1" },
|
|
29
|
-
{ name: "europe-north1", value: "europe-north1" },
|
|
30
|
-
{ name: "europe-central2", value: "europe-central2" },
|
|
31
|
-
{ name: "europe-west1", value: "europe-west1" },
|
|
32
|
-
{ name: "southamerica-west1", value: "southamerica-west1" },
|
|
33
|
-
{ name: "us-east4", value: "us-east4" },
|
|
34
|
-
{ name: "us-west1", value: "us-west1" },
|
|
35
|
-
{ name: "asia-southeast1", value: "asia-southeast1" },
|
|
36
|
-
];
|
|
37
|
-
if (setup.projectId) {
|
|
38
|
-
const locations = await (0, client_1.listLocations)(setup.projectId);
|
|
39
|
-
locationOptions = locations.map((l) => {
|
|
40
|
-
return { name: l, value: l };
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
const locationId = await (0, prompt_1.promptOnce)({
|
|
44
|
-
message: "What location would you like to deploy this service into?",
|
|
45
|
-
type: "list",
|
|
46
|
-
choices: locationOptions,
|
|
47
|
-
});
|
|
48
29
|
const connectorId = await (0, prompt_1.promptOnce)({
|
|
49
30
|
message: "What ID would you like to use for your connector?",
|
|
50
31
|
type: "input",
|
|
51
32
|
default: "my-connector",
|
|
52
33
|
});
|
|
53
|
-
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
54
|
-
if (!config.has("dataconnect")) {
|
|
55
|
-
config.set("dataconnect.source", dir);
|
|
56
|
-
config.set("dataconnect.location", locationId);
|
|
57
|
-
}
|
|
58
34
|
let cloudSqlInstanceId = "";
|
|
59
35
|
let newInstance = false;
|
|
36
|
+
let locationId = "";
|
|
60
37
|
if (setup.projectId) {
|
|
61
38
|
const instances = await cloudsql.listInstances(setup.projectId);
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
return { name: i.name, value: i.name };
|
|
39
|
+
const choices = instances.map((i) => {
|
|
40
|
+
return { name: i.name, value: i.name, location: i.region };
|
|
65
41
|
});
|
|
66
|
-
|
|
67
|
-
if (
|
|
42
|
+
const freeTrialInstanceId = await (0, freeTrial_1.checkForFreeTrialInstance)(setup.projectId);
|
|
43
|
+
if (!freeTrialInstanceId) {
|
|
44
|
+
choices.push({ name: "Create a new instance", value: "", location: "" });
|
|
45
|
+
}
|
|
46
|
+
if (instances.length) {
|
|
68
47
|
cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
69
|
-
message: `Which CloudSSQL
|
|
48
|
+
message: `Which CloudSSQL instance would you like to use?`,
|
|
70
49
|
type: "list",
|
|
71
50
|
choices,
|
|
72
51
|
});
|
|
73
52
|
}
|
|
53
|
+
locationId = choices.find((c) => c.value === cloudSqlInstanceId).location;
|
|
74
54
|
}
|
|
75
55
|
if (cloudSqlInstanceId === "") {
|
|
56
|
+
let locationOptions = [
|
|
57
|
+
{ name: "us-central1", value: "us-central1" },
|
|
58
|
+
{ name: "europe-north1", value: "europe-north1" },
|
|
59
|
+
{ name: "europe-central2", value: "europe-central2" },
|
|
60
|
+
{ name: "europe-west1", value: "europe-west1" },
|
|
61
|
+
{ name: "southamerica-west1", value: "southamerica-west1" },
|
|
62
|
+
{ name: "us-east4", value: "us-east4" },
|
|
63
|
+
{ name: "us-west1", value: "us-west1" },
|
|
64
|
+
{ name: "asia-southeast1", value: "asia-southeast1" },
|
|
65
|
+
];
|
|
66
|
+
if (setup.projectId) {
|
|
67
|
+
const locations = await (0, client_1.listLocations)(setup.projectId);
|
|
68
|
+
locationOptions = locations.map((l) => {
|
|
69
|
+
return { name: l, value: l };
|
|
70
|
+
});
|
|
71
|
+
}
|
|
76
72
|
newInstance = true;
|
|
77
73
|
cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
78
74
|
message: `What ID would you like to use for your new CloudSQL instance?`,
|
|
79
75
|
type: "input",
|
|
80
76
|
default: `dataconnect-test`,
|
|
81
77
|
});
|
|
78
|
+
locationId = await (0, prompt_1.promptOnce)({
|
|
79
|
+
message: "What location would you use for this instance?",
|
|
80
|
+
type: "list",
|
|
81
|
+
choices: locationOptions,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
85
|
+
if (!config.has("dataconnect")) {
|
|
86
|
+
config.set("dataconnect.source", dir);
|
|
87
|
+
config.set("dataconnect.location", locationId);
|
|
82
88
|
}
|
|
83
89
|
let cloudSqlDatabase = "";
|
|
84
90
|
let newDB = false;
|
|
@@ -104,7 +110,7 @@ async function doSetup(setup, config) {
|
|
|
104
110
|
default: `dataconnect`,
|
|
105
111
|
});
|
|
106
112
|
}
|
|
107
|
-
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 :
|
|
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;
|
|
108
114
|
const localConnectionString = await (0, prompt_1.promptOnce)({
|
|
109
115
|
type: "input",
|
|
110
116
|
name: "localConnectionString",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.doSetup = void 0;
|
|
3
|
+
exports.doSetup = exports.DEFAULT_POSTGRES_CONNECTION = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const _ = require("lodash");
|
|
6
6
|
const utils = require("../../utils");
|
|
@@ -8,6 +8,7 @@ const prompt_1 = require("../../prompt");
|
|
|
8
8
|
const types_1 = require("../../emulator/types");
|
|
9
9
|
const constants_1 = require("../../emulator/constants");
|
|
10
10
|
const downloadableEmulators_1 = require("../../emulator/downloadableEmulators");
|
|
11
|
+
exports.DEFAULT_POSTGRES_CONNECTION = "postgresql://localhost:5432?sslmode=disable";
|
|
11
12
|
async function doSetup(setup, config) {
|
|
12
13
|
var _a, _b, _c;
|
|
13
14
|
const choices = types_1.ALL_SERVICE_EMULATORS.map((e) => {
|
|
@@ -78,15 +79,13 @@ async function doSetup(setup, config) {
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
if (selections.emulators.includes(types_1.Emulators.DATACONNECT)) {
|
|
81
|
-
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 :
|
|
82
|
-
const localConnectionString = await (0, prompt_1.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
89
|
-
]);
|
|
82
|
+
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 : exports.DEFAULT_POSTGRES_CONNECTION;
|
|
83
|
+
const localConnectionString = await (0, prompt_1.promptOnce)({
|
|
84
|
+
type: "input",
|
|
85
|
+
name: "localConnectionString",
|
|
86
|
+
message: `What is the connection string of the local Postgres instance you would like to use with the Data Connect emulator?`,
|
|
87
|
+
default: defaultConnectionString,
|
|
88
|
+
});
|
|
90
89
|
setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
|
|
91
90
|
}
|
|
92
91
|
await (0, prompt_1.prompt)(selections, [
|
|
@@ -130,6 +130,9 @@ async function overwriteCodebase(setup, config) {
|
|
|
130
130
|
return languageSetup(setup, config);
|
|
131
131
|
}
|
|
132
132
|
async function languageSetup(setup, config) {
|
|
133
|
+
if (setup.languageOverride) {
|
|
134
|
+
return require("./" + setup.languageOverride).setup(setup, config);
|
|
135
|
+
}
|
|
133
136
|
const choices = [
|
|
134
137
|
{
|
|
135
138
|
name: "JavaScript",
|
|
@@ -174,5 +177,6 @@ async function languageSetup(setup, config) {
|
|
|
174
177
|
cbconfig.ignore = ["venv", ".git", "firebase-debug.log", "firebase-debug.*.log", "*.local"];
|
|
175
178
|
break;
|
|
176
179
|
}
|
|
180
|
+
setup.functions.languageChoice = language;
|
|
177
181
|
return require("./" + language).setup(setup, config);
|
|
178
182
|
}
|