From 2012c03ec0bd5f6af752729c9defb76d83aef553 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 27 Sep 2024 11:08:14 -0700 Subject: [PATCH 1/5] changes --- firebase-vscode/src/data-connect/service.ts | 94 +++++++++------------ src/dataconnect/dataplaneClient.ts | 13 +-- 2 files changed, 49 insertions(+), 58 deletions(-) diff --git a/firebase-vscode/src/data-connect/service.ts b/firebase-vscode/src/data-connect/service.ts index 8935f6ec096..e3d2b55b6e3 100644 --- a/firebase-vscode/src/data-connect/service.ts +++ b/firebase-vscode/src/data-connect/service.ts @@ -13,14 +13,17 @@ import { EmulatorsController } from "../core/emulators"; import { dataConnectConfigs } from "../data-connect/config"; import { firebaseRC } from "../core/config"; -import { executeGraphQL } from "../../../src/dataconnect/dataplaneClient"; +import { + dataconnectDataplaneClient, + executeGraphQL, +} from "../../../src/dataconnect/dataplaneClient"; import { ExecuteGraphqlRequest, ExecuteGraphqlResponse, ExecuteGraphqlResponseError, Impersonation, } from "../dataconnect/types"; -import { ClientResponse } from "../apiv2"; +import { Client, ClientResponse } from "../apiv2"; import { InstanceType } from "./code-lens-provider"; import { pluginLogger } from "../logger-wrapper"; import { DataConnectToolkit } from "./toolkit"; @@ -35,13 +38,12 @@ export class DataConnectService { private emulatorsController: EmulatorsController, ) {} - async servicePath( - path: string - ): Promise { + async servicePath(path: string): Promise { const dataConnectConfigsValue = await firstWhereDefined(dataConnectConfigs); // TODO: avoid calling this here and in getApiServicePathByPath const serviceId = - dataConnectConfigsValue?.tryReadValue?.findEnclosingServiceForPath(path)?.value.serviceId; + dataConnectConfigsValue?.tryReadValue?.findEnclosingServiceForPath(path) + ?.value.serviceId; const projectId = firebaseRC.value?.tryReadValue?.projects?.default; if (serviceId === undefined || projectId === undefined) { @@ -78,47 +80,35 @@ export class DataConnectService { return response.text(); } private async handleProdResponse( - clientResponse: ClientResponse< + response: ClientResponse< ExecuteGraphqlResponse | ExecuteGraphqlResponseError >, ): Promise { - if (!(clientResponse.status >= 200 && clientResponse.status < 300)) { + if (!(response.status >= 200 && response.status < 300)) { const errorResponse = - clientResponse as ClientResponse; + response as ClientResponse; throw new DataConnectError( - `Request failed with status ${clientResponse.status}`, - errorResponse.body.error.message, + `Prod Request failed with status ${response.status}: message ${errorResponse?.body?.error?.message}`, ); } - const successResponse = - clientResponse as ClientResponse; + const successResponse = response as ClientResponse; return successResponse.body; } - private async handleValidResponse( - response: Response, + private async handleEmulatorResponse( + response: ClientResponse< + ExecuteGraphqlResponse | ExecuteGraphqlResponseError + >, ): Promise { - const json = await this.decodeResponse(response, "application/json"); - assertExecutionResult(json); - - return json; - } - - private async handleInvalidResponse(response: Response): Promise { - const cause = await this.decodeResponse(response); - - throw new DataConnectError( - `Request failed with status ${response.status}`, - cause, - ); - } - - private handleResponse(response: Response): Promise { - if (response.status >= 200 && response.status < 300) { - return this.handleValidResponse(response); + if (!(response.status >= 200 && response.status < 300)) { + const errorResponse = + response as ClientResponse; + throw new DataConnectError( + `Prod Request failed with status ${response.status}: message ${errorResponse?.body?.error?.message}`, + ); } - - return this.handleInvalidResponse(response); + const successResponse = response as ClientResponse; + return successResponse.body; } /** Encode a body while handling the fact that "variables" is raw JSON. @@ -243,23 +233,23 @@ export class DataConnectService { extensions: this._auth(), }); if (params.instance === InstanceType.PRODUCTION) { - const resp = await executeGraphQL(servicePath, prodBody); + const client = dataconnectDataplaneClient(); + const resp = await executeGraphQL(client, servicePath, prodBody); return this.handleProdResponse(resp); } else { - const resp = await fetch( - (await this.emulatorsController.getLocalEndpoint()) + - `/v1beta/${servicePath}:executeGraphql`, - { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - "x-mantle-admin": "all", - }, - body, - }, - ); - return this.handleResponse(resp); + const endpoint = this.emulatorsController.getLocalEndpoint(); + if (!endpoint) { + throw new DataConnectError( + `Emulator isn't running. Please start your emulator!`, + ); + } + const client = new Client({ + urlPrefix: endpoint, + apiVersion: "v1beta", + auth: true, + }); + const resp = await executeGraphQL(client, servicePath, prodBody); + return this.handleEmulatorResponse(resp); } } @@ -274,9 +264,9 @@ function parseVariableString(variables: string): Record { } try { return JSON.parse(variables); - } catch(e: any) { + } catch (e: any) { throw new Error( - "Unable to parse variables as JSON. Double check that that there are no unmatched braces or quotes, or unqouted keys in the variables pane." + "Unable to parse variables as JSON. Double check that that there are no unmatched braces or quotes, or unqouted keys in the variables pane.", ); } } diff --git a/src/dataconnect/dataplaneClient.ts b/src/dataconnect/dataplaneClient.ts index 305d4a8b4c9..75adf766be0 100644 --- a/src/dataconnect/dataplaneClient.ts +++ b/src/dataconnect/dataplaneClient.ts @@ -1,18 +1,19 @@ import { dataconnectOrigin } from "../api"; -import { Client } from "../apiv2"; +import { Client, ClientResponse } from "../apiv2"; import * as types from "./types"; -const DATACONNECT_API_VERSION = "v1beta"; +export const DATACONNECT_API_VERSION = "v1beta"; -const dataconnectDataplaneClient = () => - new Client({ +export function dataconnectDataplaneClient(): Client { + return new Client({ urlPrefix: dataconnectOrigin(), apiVersion: DATACONNECT_API_VERSION, auth: true, }); +} -export async function executeGraphQL(servicePath: string, body: types.ExecuteGraphqlRequest) { - const res = await dataconnectDataplaneClient().post< +export async function executeGraphQL(client: Client, servicePath: string, body: types.ExecuteGraphqlRequest): Promise> { + const res = await client.post< types.ExecuteGraphqlRequest, types.ExecuteGraphqlResponse | types.ExecuteGraphqlResponseError >(`${servicePath}:executeGraphql`, body, { resolveOnHTTPError: true }); From 6058e57ecad73a73c9d3e83aba07432cea1ba8ac Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 27 Sep 2024 11:16:57 -0700 Subject: [PATCH 2/5] improve error message --- firebase-vscode/src/data-connect/service.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/firebase-vscode/src/data-connect/service.ts b/firebase-vscode/src/data-connect/service.ts index e3d2b55b6e3..d0b932ece4a 100644 --- a/firebase-vscode/src/data-connect/service.ts +++ b/firebase-vscode/src/data-connect/service.ts @@ -23,7 +23,7 @@ import { ExecuteGraphqlResponseError, Impersonation, } from "../dataconnect/types"; -import { Client, ClientResponse } from "../apiv2"; +import { Client, ClientResponse } from "../../../src/apiv2"; import { InstanceType } from "./code-lens-provider"; import { pluginLogger } from "../logger-wrapper"; import { DataConnectToolkit } from "./toolkit"; @@ -38,12 +38,13 @@ export class DataConnectService { private emulatorsController: EmulatorsController, ) {} - async servicePath(path: string): Promise { + async servicePath( + path: string + ): Promise { const dataConnectConfigsValue = await firstWhereDefined(dataConnectConfigs); // TODO: avoid calling this here and in getApiServicePathByPath const serviceId = - dataConnectConfigsValue?.tryReadValue?.findEnclosingServiceForPath(path) - ?.value.serviceId; + dataConnectConfigsValue?.tryReadValue?.findEnclosingServiceForPath(path)?.value.serviceId; const projectId = firebaseRC.value?.tryReadValue?.projects?.default; if (serviceId === undefined || projectId === undefined) { @@ -88,7 +89,7 @@ export class DataConnectService { const errorResponse = response as ClientResponse; throw new DataConnectError( - `Prod Request failed with status ${response.status}: message ${errorResponse?.body?.error?.message}`, + `Prod Request failed with status ${response.status}\nMessage ${errorResponse?.body?.error?.message}`, ); } const successResponse = response as ClientResponse; @@ -104,7 +105,7 @@ export class DataConnectService { const errorResponse = response as ClientResponse; throw new DataConnectError( - `Prod Request failed with status ${response.status}: message ${errorResponse?.body?.error?.message}`, + `Prod Request failed with status ${response.status}\nMessage ${errorResponse?.body?.error?.message}`, ); } const successResponse = response as ClientResponse; @@ -264,9 +265,9 @@ function parseVariableString(variables: string): Record { } try { return JSON.parse(variables); - } catch (e: any) { + } catch(e: any) { throw new Error( - "Unable to parse variables as JSON. Double check that that there are no unmatched braces or quotes, or unqouted keys in the variables pane.", + "Unable to parse variables as JSON. Double check that that there are no unmatched braces or quotes, or unqouted keys in the variables pane." ); } } From 87dda8107e7b5a723e18f961ae7ec2d0268a9a57 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 27 Sep 2024 11:21:15 -0700 Subject: [PATCH 3/5] m --- firebase-vscode/src/data-connect/service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-vscode/src/data-connect/service.ts b/firebase-vscode/src/data-connect/service.ts index d0b932ece4a..3f95bf1eb72 100644 --- a/firebase-vscode/src/data-connect/service.ts +++ b/firebase-vscode/src/data-connect/service.ts @@ -105,7 +105,7 @@ export class DataConnectService { const errorResponse = response as ClientResponse; throw new DataConnectError( - `Prod Request failed with status ${response.status}\nMessage ${errorResponse?.body?.error?.message}`, + `Emulator Request failed with status ${response.status}\nMessage ${errorResponse?.body?.error?.message}`, ); } const successResponse = response as ClientResponse; From 33df9245bbf30af7fca753ddb00afd255cbecace Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 27 Sep 2024 11:28:23 -0700 Subject: [PATCH 4/5] feedbacks --- firebase-vscode/src/data-connect/service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-vscode/src/data-connect/service.ts b/firebase-vscode/src/data-connect/service.ts index 3f95bf1eb72..bc758a21d06 100644 --- a/firebase-vscode/src/data-connect/service.ts +++ b/firebase-vscode/src/data-connect/service.ts @@ -16,6 +16,7 @@ import { firebaseRC } from "../core/config"; import { dataconnectDataplaneClient, executeGraphQL, + DATACONNECT_API_VERSION, } from "../../../src/dataconnect/dataplaneClient"; import { ExecuteGraphqlRequest, @@ -246,8 +247,7 @@ export class DataConnectService { } const client = new Client({ urlPrefix: endpoint, - apiVersion: "v1beta", - auth: true, + apiVersion: DATACONNECT_API_VERSION, }); const resp = await executeGraphQL(client, servicePath, prodBody); return this.handleEmulatorResponse(resp); From 7dfed30d4d2c4fe6cbeb372fc5fca8b1580234ba Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 27 Sep 2024 11:40:49 -0700 Subject: [PATCH 5/5] m --- src/dataconnect/dataplaneClient.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dataconnect/dataplaneClient.ts b/src/dataconnect/dataplaneClient.ts index 75adf766be0..eca756160ae 100644 --- a/src/dataconnect/dataplaneClient.ts +++ b/src/dataconnect/dataplaneClient.ts @@ -12,7 +12,11 @@ export function dataconnectDataplaneClient(): Client { }); } -export async function executeGraphQL(client: Client, servicePath: string, body: types.ExecuteGraphqlRequest): Promise> { +export async function executeGraphQL( + client: Client, + servicePath: string, + body: types.ExecuteGraphqlRequest, +): Promise> { const res = await client.post< types.ExecuteGraphqlRequest, types.ExecuteGraphqlResponse | types.ExecuteGraphqlResponseError