From d6d8028846ebf4f103cacfbf977a67cba403ef45 Mon Sep 17 00:00:00 2001 From: joehan Date: Mon, 20 May 2024 14:18:22 -0400 Subject: [PATCH] Dataconnect (#7193) * Cloud SQL create and update polish (#7159) * improve logged message execute SQL commands (#7156) * Error handling and logging when linking CSQL instance (#7158) * Display accurate message for INACCESSIBLE_SCHEMA error (#7157) * handle INACCESSIBLE_SCHEMA case separately * error msg * m * m * m * merge * feedbacks * Jh idx no metadata calls (#7179) * Avoid triggering metadata server calls in idx * Remove outdated service account check * Update Firebase Logo (#7180) * unleash turtles (#7174) * unleash turtles * Update CHANGELOG.md * Flag flip for gen kit (#7175) * Flag flip for gen kit * Update CHANGELOG.md * update icons --------- Co-authored-by: Bryan Kendall Co-authored-by: joehan * Use client instead of size 1 pool (#7182) * Fix schema validation (#7185) * Fix schema validation * Also depend on redhat.yaml * fix typo --------- Co-authored-by: Fred Zhang Co-authored-by: Harold Shen Co-authored-by: Bryan Kendall --- firebase-vscode/package.json | 9 +- firebase-vscode/resources/Monicons.woff | Bin 1932 -> 10476 bytes firebase-vscode/resources/firebase_logo.png | Bin 17944 -> 47145 bytes firebase-vscode/src/cli.ts | 17 +-- firebase-vscode/src/core/project.ts | 3 +- src/dataconnect/client.ts | 3 +- src/dataconnect/errors.ts | 33 ++--- src/dataconnect/provisionCloudSql.ts | 44 ++++--- src/dataconnect/schemaMigration.ts | 129 ++++++++++++++------ src/dataconnect/types.ts | 7 +- src/gcp/cloudsql/cloudsqladmin.ts | 2 + src/gcp/cloudsql/connect.ts | 22 ++-- src/gcp/cloudsql/types.ts | 2 + src/requireAuth.ts | 3 + 14 files changed, 176 insertions(+), 98 deletions(-) diff --git a/firebase-vscode/package.json b/firebase-vscode/package.json index c3521fb3796..e0e557bdff3 100644 --- a/firebase-vscode/package.json +++ b/firebase-vscode/package.json @@ -14,7 +14,8 @@ "Other" ], "extensionDependencies": [ - "graphql.vscode-graphql-syntax" + "graphql.vscode-graphql-syntax", + "redhat.vscode-yaml" ], "activationEvents": [ "onStartupFinished", @@ -110,7 +111,7 @@ "mono-firebase": { "description": "Firebase icon", "default": { - "fontPath": "./resources/Monicons.woff", + "fontPath": "./resources/monicons.woff", "fontCharacter": "\\F101" } }, @@ -180,11 +181,11 @@ }, { "fileMatch": "dataconnect.yaml", - "url": "./schema/dataconnect-yaml.json" + "url": "./dist/schema/dataconnect-yaml.json" }, { "fileMatch": "connector.yaml", - "url": "./schema/connector-yaml.json" + "url": "./dist/schema/connector-yaml.json" } ] }, diff --git a/firebase-vscode/resources/Monicons.woff b/firebase-vscode/resources/Monicons.woff index e6735ccb638c7ef0b6a97b3955a94cbd8238a4f8..815694ade6bb5f9e2a62c84c369a65e1a20783a2 100644 GIT binary patch literal 10476 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 N$O;Brf{6nI`#&|TCaM4c delta 1890 zcmZWpc{JNu8~!EH*hyPk(^y8;Ud4259m`k}TSQ`+RxOE;L93`(6BCryuBa+uT3c1W z)UlN5Vo5QT3ZX{TR#LTg47D|i^8NIj@2~Hk^PGF1=f3xO?>+b4cNw20&4|IF(KvT^ z8~_Ms0g?dN#AN`;VgLWl28~t$0GK@pG5`qePU53sXtX;PTpJEXp8$bkKHPeSv9&`3 z0GtJKRS;^xFMuM(73T)V>4IDjgb`n1wG`v#!LSAc2SMQ?i1@LR$qJm?SwjGTB!V0? z0f@M}J|W~#5|{w`1mtvZE@k#_&BmZWQUEyC2em^}BPFj3{{(>|sCk1N2|^9F4EY!o zN{s~*A8N-zsKVHQRVXPI^n-x9F8~KcI3Vo`TqT7Df+7dxpTPcERF|h)cvvJAJndl; zIS?ub)4++?QZg}sNCalc{esn=jR|@npF#&@NKgOF!Jvx4q1*zI2wejR%VHe`Dv%!_ z(goI#(+mh20v8gokHnIKh=boa-P-`?Exf?$r|=_URvd9)b6&It0AfoMat&Ny1_-+j zV{@2k%!|satLHRPNvA%qxgaw}8}~Q*-YK7C zOIq;5eG419jtO4L)(s*D>N}Yc*nyfg)SrxPBVXc@wwX^tXzlUylX>cOCW4o-H#B}m zzT0d)Ik2>C=v%+PA-|QCl6Le(g|Hj}6{D5!i_qt%n743RZ+v(%eQd|yT&|xg6syTl z8QHl(et%9m$h@JV|BW&k-3F&ZzOLVhJ zR=E6Rfh8P!>uYBO(PNI_qpyXA`i2EizQ+H!g`VmyD^6e=MM>Eqgx}QA?EM~G$irW* zbhubmU0KAM+53XoYo>Q{UGNlTM%1nAqw_B}IvaKyV9r^R8Hd+M0j?j!D5xdMoPvSx zMb8kjeYmMqcb_1cUu7&}DA7%I6WOBamQSlx$45fCA65I46)W!&T*fe z@?nC;6O`SfQEr>$XjZE~)}cl(Yg41s8*Lq!b+YfPHYe`<5%|xN3l1I19VNXAcI2|i zPReK1?qp{McpHC{oy1EDY9E10dJ5m_RTH52cDab9XRW@WKA zCW>BcssBf81#pxRO8`Q2Nz)*FSgVk|5PsDC&gf%9$?$Po@AE3lV&73XvMe<9p>9VLYm5}#jPZ_OXPhC z5GnLLMKFhESlZoxE43P=9Cg*^w@nD@ffn?+a&~#FDQb>vRV^u^*E|zeTSjno+EX>$7Yn)u0ACl2Syxq9c!GuyT zjQd@MerAYp%`%NV@|qOJ!+b`$s1E*`?a`f#Fb$ak~t;5 zD9?P`cWQ}kUd;>kicu#-12Bt(_GhW6lZ-Srj@lLYE`lRAJnPT#&`xu15t(0$RjeC! zte2mvJZD!bF|W;w;0-1&()-|!!=`;7?gXN+jF09CT!Vvd^v1rvy63No7+1%}XlAtuphkRpNabg@c>o z7{fnCN;~bYN-Qp5ylrnVR5V*94!l+(1ff#va>jFfiQscXbmYF1mfF%K(Xap3Ean!s zhv|GftFIZirkLLviP(=9&%3$K_cr|$DFQQLNz`A>{ZCUWbND5kOMBK3n43Fhx5JpT zW@QjAwr0xw-twh1zehm8deS6+`B?1i#h@*T$2+N4w!I$bwr*Jhmx_kLulBu?@*(~w zR47nuv`TJXQc;pLhXTno08922!0+%op3a;QP1HvSX&`@+ZWa4?3?gQw}J zo+)0qf=Yb^@mjnx$}@%V@YU?DqyHY3*FqgHaJfgT%zTv@-<*V>9HMeDfm@d=o9gaQ z8633?nS!r^_56Pu@V`aYClUJ`bYGMCL?R?ZfA3%b$S3LYSdOU~XNZX~piK#$^gW)= qX0t`QO%h=+7+~xVg?;xR5(9u_nnlbD_NJLqgn}es-*IFO^#3oN-6@9v diff --git a/firebase-vscode/resources/firebase_logo.png b/firebase-vscode/resources/firebase_logo.png index e3e46f779392352ee4b67e57bb205b54e2f325a5..c5e09f683f342d953920e5d5eda77fb85db77800 100644 GIT binary patch literal 47145 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

`8 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 literal 17944 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 t { + // NOTE(fredzqm): Don't force delete yet. Backend would leave orphaned resources. const op = await dataconnectClient().delete( - `projects/${projectId}/locations/${locationId}/services/${serviceId}?force=true`, + `projects/${projectId}/locations/${locationId}/services/${serviceId}`, ); const pollRes = await operationPoller.pollOperation({ apiOrigin: dataconnectOrigin(), diff --git a/src/dataconnect/errors.ts b/src/dataconnect/errors.ts index 5f6f9666580..f14a9b8c5b5 100644 --- a/src/dataconnect/errors.ts +++ b/src/dataconnect/errors.ts @@ -5,28 +5,27 @@ const PRECONDITION_ERROR_TYPESTRING = "type.googleapis.com/google.rpc.Preconditi const INCOMPATIBLE_CONNECTOR_TYPE = "INCOMPATIBLE_CONNECTOR"; export function getIncompatibleSchemaError(err: any): IncompatibleSqlSchemaError | undefined { - const original = err.context?.body?.error || err.orignal; - if (!original) { - // If we can't get the original, rethrow so we don't cover up the original error. - throw err; + const incompatibles = errorDetails(err, INCOMPATIBLE_SCHEMA_ERROR_TYPESTRING); + if (incompatibles.length === 0) { + return undefined; } - const details: any[] = original.details; - const incompatibles = details.filter((d) => - d["@type"]?.includes(INCOMPATIBLE_SCHEMA_ERROR_TYPESTRING), - ); // Should never get multiple incompatible schema errors - return incompatibles[0]; + const incompatible = incompatibles[0]; + // Extract the violation type from the precondition error detail. + const preconditionErrs = errorDetails(err, PRECONDITION_ERROR_TYPESTRING); + const violationTypes = (incompatible.violationType = preconditionErrs + .flatMap((preCondErr) => preCondErr.violations) + .flatMap((viol) => viol.type) + .filter((type) => type === "INACCESSIBLE_SCHEMA" || type === "INCOMPATIBLE_SCHEMA")); + incompatible.violationType = violationTypes[0]; + return incompatible; } // Note - the backend just includes file name, not the name of the connector resource in the GQLerror extensions. // so we don't use this yet. Ideally, we'd just include connector name in the extensions. export function getInvalidConnectors(err: any): string[] { + const preconditionErrs = errorDetails(err, PRECONDITION_ERROR_TYPESTRING); const invalidConns: string[] = []; - const original = err.context?.body?.error || err?.orignal; - const details: any[] = original?.details; - const preconditionErrs = details?.filter((d) => - d["@type"]?.includes(PRECONDITION_ERROR_TYPESTRING), - ); for (const preconditionErr of preconditionErrs) { const incompatibleConnViolation = preconditionErr?.violations?.filter( (v: { type: string }) => v.type === INCOMPATIBLE_CONNECTOR_TYPE, @@ -36,3 +35,9 @@ export function getInvalidConnectors(err: any): string[] { } return invalidConns; } + +function errorDetails(err: any, ofType: string): any[] { + const original = err.context?.body?.error || err?.original; + const details: any[] = original?.details; + return details?.filter((d) => d["@type"]?.includes(ofType)) || []; +} diff --git a/src/dataconnect/provisionCloudSql.ts b/src/dataconnect/provisionCloudSql.ts index 18d7a439f6e..a8e1ec14d20 100755 --- a/src/dataconnect/provisionCloudSql.ts +++ b/src/dataconnect/provisionCloudSql.ts @@ -27,13 +27,14 @@ export async function provisionCloudSql(args: { const existingInstance = await cloudSqlAdminClient.getInstance(projectId, instanceId); silent || utils.logLabeledBullet("dataconnect", `Found existing instance ${instanceId}.`); connectionName = existingInstance?.connectionName || ""; - if (!checkInstanceConfig(existingInstance, enableGoogleMlIntegration)) { - // TODO: Return message from checkInstanceConfig to explain exactly what changes are made + const why = getUpdateReason(existingInstance, enableGoogleMlIntegration); + if (why) { silent || utils.logLabeledBullet( "dataconnect", `Instance ${instanceId} settings not compatible with Firebase Data Connect. ` + - `Updating instance to enable Cloud IAM authentication and public IP. This may take a few minutes...`, + `Updating instance. This may take a few minutes...` + + why, ); await promiseWithSpinner( () => @@ -77,11 +78,21 @@ export async function provisionCloudSql(args: { try { await cloudSqlAdminClient.getDatabase(projectId, instanceId, databaseId); silent || utils.logLabeledBullet("dataconnect", `Found existing database ${databaseId}.`); - } catch (err) { - silent || - utils.logLabeledBullet("dataconnect", `Database ${databaseId} not found, creating it now...`); - await cloudSqlAdminClient.createDatabase(projectId, instanceId, databaseId); - silent || utils.logLabeledBullet("dataconnect", `Database ${databaseId} created.`); + } catch (err: any) { + if (err.status === 404) { + // Create the database if not found. + silent || + utils.logLabeledBullet( + "dataconnect", + `Database ${databaseId} not found, creating it now...`, + ); + await cloudSqlAdminClient.createDatabase(projectId, instanceId, databaseId); + silent || utils.logLabeledBullet("dataconnect", `Database ${databaseId} created.`); + } else { + // Skip it if the database is not accessible. + // Possible that the CSQL instance is in the middle of something. + silent || utils.logLabeledWarning("dataconnect", `Database ${databaseId} is not accessible.`); + } } if (enableGoogleMlIntegration) { await grantRolesToCloudSqlServiceAccount(projectId, instanceId, [GOOGLE_ML_INTEGRATION_ROLE]); @@ -92,26 +103,24 @@ export async function provisionCloudSql(args: { /** * Validate that existing CloudSQL instances have the necessary settings. */ -export function checkInstanceConfig( - instance: Instance, - requireGoogleMlIntegration: boolean, -): boolean { +export function getUpdateReason(instance: Instance, requireGoogleMlIntegration: boolean): string { + let reason = ""; const settings = instance.settings; // CloudSQL instances must have public IP enabled to be used with Firebase Data Connect. if (!settings.ipConfiguration?.ipv4Enabled) { - return false; + reason += "\n - to enable public IP."; } if (requireGoogleMlIntegration) { if (!settings.enableGoogleMlIntegration) { - return false; + reason += "\n - to enable Google ML integration."; } if ( !settings.databaseFlags?.some( (f) => f.name === "cloudsql.enable_google_ml_integration" && f.value === "on", ) ) { - return false; + reason += "\n - to enable Google ML integration database flag."; } } @@ -120,6 +129,9 @@ export function checkInstanceConfig( settings.databaseFlags?.some( (f) => f.name === "cloudsql.iam_authentication" && f.value === "on", ) ?? false; + if (!isIamEnabled) { + reason += "\n - to enable IAM authentication database flag."; + } - return isIamEnabled; + return reason; } diff --git a/src/dataconnect/schemaMigration.ts b/src/dataconnect/schemaMigration.ts index 385dc2eb9d4..cf49c33a092 100644 --- a/src/dataconnect/schemaMigration.ts +++ b/src/dataconnect/schemaMigration.ts @@ -10,26 +10,40 @@ import { Schema } from "./types"; import { Options } from "../options"; import { FirebaseError } from "../error"; import { needProjectId } from "../projectUtils"; -import { logLabeledWarning, logLabeledSuccess } from "../utils"; +import { logLabeledBullet, logLabeledWarning, logLabeledSuccess } from "../utils"; import * as errors from "./errors"; export async function diffSchema(schema: Schema): Promise { const { serviceName, instanceName, databaseId } = getIdentifiers(schema); - await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId); + await ensureServiceIsConnectedToCloudSql( + serviceName, + instanceName, + databaseId, + /* linkIfNotConnected=*/ false, + ); try { await upsertSchema(schema, /** validateOnly=*/ true); + logLabeledSuccess("dataconnect", `Database schema is up to date.`); } catch (err: any) { + if (err.status !== 400) { + throw err; + } const invalidConnectors = errors.getInvalidConnectors(err); + const incompatible = errors.getIncompatibleSchemaError(err); + if (!incompatible && !invalidConnectors.length) { + // If we got a different type of error, throw it + throw err; + } + + // Display failed precondition errors nicely. if (invalidConnectors.length) { displayInvalidConnectors(invalidConnectors); } - const incompatible = errors.getIncompatibleSchemaError(err); if (incompatible) { displaySchemaChanges(incompatible); return incompatible.diffs; } } - logLabeledSuccess("dataconnect", `Database schema is up to date.`); return []; } @@ -42,17 +56,27 @@ export async function migrateSchema(args: { const { options, schema, allowNonInteractiveMigration, validateOnly } = args; const { serviceName, instanceId, instanceName, databaseId } = getIdentifiers(schema); - await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId); + await ensureServiceIsConnectedToCloudSql( + serviceName, + instanceName, + databaseId, + /* linkIfNotConnected=*/ true, + ); try { await upsertSchema(schema, validateOnly); logger.debug(`Database schema was up to date for ${instanceId}:${databaseId}`); } catch (err: any) { + if (err.status !== 400) { + throw err; + } + // Parse and handle failed precondition errors, then retry. const incompatible = errors.getIncompatibleSchemaError(err); const invalidConnectors = errors.getInvalidConnectors(err); if (!incompatible && !invalidConnectors.length) { // If we got a different type of error, throw it throw err; } + const shouldDeleteInvalidConnectors = await promptForInvalidConnectorError( options, invalidConnectors, @@ -61,7 +85,7 @@ export async function migrateSchema(args: { if (!shouldDeleteInvalidConnectors && invalidConnectors.length) { const cmd = suggestedCommand(serviceName, invalidConnectors); throw new FirebaseError( - `Command aborted. Try deploying compatible connectors first with ${clc.bold(cmd)}`, + `Command aborted. Try deploying those connectors first with ${clc.bold(cmd)}`, ); } const migrationMode = incompatible @@ -266,44 +290,61 @@ function displayInvalidConnectors(invalidConnectors: string[]) { // If a service has never had a schema with schemaValidation=strict // (ie when users create a service in console), -// the backend will not have the necesary permissions to check cSQL for differences. +// the backend will not have the necessary permissions to check cSQL for differences. // We fix this by upserting the currently deployed schema with schemaValidation=strict, async function ensureServiceIsConnectedToCloudSql( serviceName: string, instanceId: string, databaseId: string, + linkIfNotConnected: boolean, ) { let currentSchema: Schema; try { currentSchema = await getSchema(serviceName); } catch (err: any) { - if (err.status === 404) { - // If no schema has been deployed yet, deploy an empty one to get connectivity. - currentSchema = { - name: `${serviceName}/schemas/${SCHEMA_ID}`, - source: { - files: [], - }, - primaryDatasource: { - postgresql: { - database: databaseId, - cloudSql: { - instance: instanceId, - }, - }, - }, - }; - } else { + if (err.status !== 404) { throw err; } + if (!linkIfNotConnected) { + logLabeledWarning("dataconnect", `Not yet linked to the Cloud SQL instance.`); + return; + } + // TODO: make this prompt + // Should we upsert service here as well? so `database:sql:migrate` work for new service as well. + logLabeledBullet("dataconnect", `Linking the Cloud SQL instance...`); + // If no schema has been deployed yet, deploy an empty one to get connectivity. + currentSchema = { + name: `${serviceName}/schemas/${SCHEMA_ID}`, + source: { + files: [], + }, + primaryDatasource: { + postgresql: { + database: databaseId, + cloudSql: { + instance: instanceId, + }, + }, + }, + }; } - if ( - !currentSchema.primaryDatasource.postgresql || - currentSchema.primaryDatasource.postgresql.schemaValidation === "STRICT" - ) { + const postgresql = currentSchema.primaryDatasource.postgresql; + if (postgresql?.cloudSql.instance !== instanceId) { + logLabeledWarning( + "dataconnect", + `Switching connected Cloud SQL instance\nFrom ${postgresql?.cloudSql.instance}\nTo ${instanceId}`, + ); + } + if (postgresql?.database !== databaseId) { + logLabeledWarning( + "dataconnect", + `Switching connected Postgres database from ${postgresql?.database} to ${databaseId}`, + ); + } + if (!postgresql || postgresql.schemaValidation === "STRICT") { return; } - currentSchema.primaryDatasource.postgresql.schemaValidation = "STRICT"; + postgresql.schemaValidation = "STRICT"; try { await upsertSchema(currentSchema, /** validateOnly=*/ false); } catch (err: any) { @@ -315,11 +356,31 @@ async function ensureServiceIsConnectedToCloudSql( } function displaySchemaChanges(error: IncompatibleSqlSchemaError) { - const message = - "Your new schema is incompatible with the schema of your CloudSQL database. " + - "The following SQL statements will migrate your database schema to match your new Data Connect schema.\n" + - error.diffs.map(toString).join("\n"); - logLabeledWarning("dataconnect", message); + switch (error.violationType) { + case "INCOMPATIBLE_SCHEMA": + { + const message = + "Your new schema is incompatible with the schema of your CloudSQL database. " + + "The following SQL statements will migrate your database schema to match your new Data Connect schema.\n" + + error.diffs.map(toString).join("\n"); + logLabeledWarning("dataconnect", message); + } + break; + case "INACCESSIBLE_SCHEMA": + { + const message = + "Cannot access your CloudSQL database to validate schema. " + + "The following SQL statements can setup a new database schema.\n" + + error.diffs.map(toString).join("\n"); + logLabeledWarning("dataconnect", message); + logLabeledWarning("dataconnect", "Some SQL resources may already exist."); + } + break; + default: + throw new FirebaseError( + `Unknown schema violation type: ${error.violationType}, IncompatibleSqlSchemaError: ${error}`, + ); + } } function toString(diff: Diff) { diff --git a/src/dataconnect/types.ts b/src/dataconnect/types.ts index 7efaee5405f..c2d776fcc58 100644 --- a/src/dataconnect/types.ts +++ b/src/dataconnect/types.ts @@ -48,12 +48,15 @@ export interface File { content: string; } -// An error indicating that the SQL database schema is incomptible with a data connect schema. +// An error indicating that the SQL database schema is incompatible with a data connect schema. export interface IncompatibleSqlSchemaError { - // A list of differences between the two schema with instrucitons how to resolve them. + // A list of differences between the two schema with instructions how to resolve them. diffs: Diff[]; // Whether any of the changes included are destructive. destructive: boolean; + + // The failed precondition validation type. + violationType: "INCOMPATIBLE_SCHEMA" | "INACCESSIBLE_SCHEMA" | string; } export interface Diff { diff --git a/src/gcp/cloudsql/cloudsqladmin.ts b/src/gcp/cloudsql/cloudsqladmin.ts index c61c0aaeb86..b5580aa7b2f 100755 --- a/src/gcp/cloudsql/cloudsqladmin.ts +++ b/src/gcp/cloudsql/cloudsqladmin.ts @@ -51,6 +51,8 @@ export async function createInstance( userLabels: { "firebase-data-connect": "ft" }, insightsConfig: { queryInsightsEnabled: true, + queryPlansPerMinute: 5, // Match the default settings + queryStringLength: 1024, // Match the default settings }, }, }); diff --git a/src/gcp/cloudsql/connect.ts b/src/gcp/cloudsql/connect.ts index 5068b8b7d01..5bb78a1b7bc 100644 --- a/src/gcp/cloudsql/connect.ts +++ b/src/gcp/cloudsql/connect.ts @@ -28,11 +28,11 @@ export async function execute( const connectionName = instance.connectionName; if (!connectionName) { throw new FirebaseError( - `Could not get instance conection string for ${opts.instanceId}:${opts.databaseId}`, + `Could not get instance connection string for ${opts.instanceId}:${opts.databaseId}`, ); } let connector: Connector; - let pool: pg.Pool; + let client: pg.Client; switch (user.type) { case "CLOUD_IAM_USER": { connector = new Connector({ @@ -43,11 +43,10 @@ export async function execute( ipType: IpAddressTypes.PUBLIC, authType: AuthTypes.IAM, }); - pool = new pg.Pool({ + client = new pg.Client({ ...clientOpts, user: opts.username, database: opts.databaseId, - max: 1, }); break; } @@ -61,16 +60,15 @@ export async function execute( ipType: IpAddressTypes.PUBLIC, authType: AuthTypes.IAM, }); - pool = new pg.Pool({ + client = new pg.Client({ ...clientOpts, user: opts.username, database: opts.databaseId, - max: 1, }); break; } default: { - // cSQL doesn't return user.type for BUILT_IN users... + // Cloud SQL doesn't return user.type for BUILT_IN users... if (!opts.password) { throw new FirebaseError(`Cannot connect as BUILT_IN user without a password.`); } @@ -81,27 +79,27 @@ export async function execute( instanceConnectionName: connectionName, ipType: IpAddressTypes.PUBLIC, }); - pool = new pg.Pool({ + client = new pg.Client({ ...clientOpts, user: opts.username, password: opts.password, database: opts.databaseId, - max: 1, }); break; } } + logFn(`Logged in as ${opts.username}`); for (const s of sqlStatements) { - logFn(`Executing: '${s}' as ${opts.username}`); + logFn(`Executing: '${s}'`); try { - await pool.query(s); + await client.query(s); } catch (err) { throw new FirebaseError(`Error executing ${err}`); } } - await pool.end(); + await client.end(); connector.close(); } diff --git a/src/gcp/cloudsql/types.ts b/src/gcp/cloudsql/types.ts index e4fb71e0879..76cee9748cc 100644 --- a/src/gcp/cloudsql/types.ts +++ b/src/gcp/cloudsql/types.ts @@ -56,6 +56,8 @@ export interface DatabaseFlag { interface InsightsConfig { queryInsightsEnabled: boolean; + queryPlansPerMinute: number; + queryStringLength: number; } // TODO: Consider splitting off return only fields and input fields into different types. diff --git a/src/requireAuth.ts b/src/requireAuth.ts index 293d6924821..09329fc058d 100644 --- a/src/requireAuth.ts +++ b/src/requireAuth.ts @@ -37,6 +37,9 @@ function getAuthClient(config: GoogleAuthOptions): GoogleAuth { * @param authScopes scopes to be obtained. */ async function autoAuth(options: Options, authScopes: string[]): Promise { + if (process.env.MONOSPACE_ENV) { + throw new FirebaseError("autoAuth not yet implemented for IDX. Please run 'firebase login'"); + } const client = getAuthClient({ scopes: authScopes, projectId: options.project }); const token = await client.getAccessToken(); token !== null ? apiv2.setAccessToken(token) : false;