diff --git a/firebase-vscode/CHANGELOG.md b/firebase-vscode/CHANGELOG.md index 721e2fd3d1b..d3419b9d5b0 100644 --- a/firebase-vscode/CHANGELOG.md +++ b/firebase-vscode/CHANGELOG.md @@ -1,5 +1,7 @@ ## NEXT +- [Fixed] Fixed an issue where Add data and Read data would generate operations in the wrong folder + ## 0.10.6 - Updated internal firebase-tools dependency to 13.23.1 diff --git a/firebase-vscode/src/data-connect/ad-hoc-mutations.ts b/firebase-vscode/src/data-connect/ad-hoc-mutations.ts index e20fe750d11..3fe6511465f 100644 --- a/firebase-vscode/src/data-connect/ad-hoc-mutations.ts +++ b/firebase-vscode/src/data-connect/ad-hoc-mutations.ts @@ -16,6 +16,8 @@ import { import { checkIfFileExists, upsertFile } from "./file-utils"; import { DataConnectService } from "./service"; import { DATA_CONNECT_EVENT_NAME } from "../analytics"; +import { dataConnectConfigs } from "./config"; +import { firstWhereDefined } from "../utils/signal"; export function registerAdHoc( dataConnectService: DataConnectService, @@ -47,6 +49,7 @@ export function registerAdHoc( async function schemaReadData( document: DocumentNode, ast: ObjectTypeDefinitionNode, + documentPath: string, ) { // TODO(rrousselGit) - this is a temporary solution due to the lack of a "schema". // As such, we hardcoded the list of allowed primitives. @@ -62,8 +65,12 @@ export function registerAdHoc( "Any", ]); - const basePath = vscode.workspace.rootPath + "/dataconnect/"; - const filePath = vscode.Uri.file(`${basePath}${ast.name.value}_read.gql`); + const configs = await firstWhereDefined(dataConnectConfigs); + const dataconnectConfig = + configs.tryReadValue?.findEnclosingServiceForPath(documentPath); + + const basePath = dataconnectConfig?.path; + const filePath = vscode.Uri.file(`${basePath}/${ast.name.value}_read.gql`); // Recursively build a query for the object type. // Returns undefined if the query is empty. @@ -136,12 +143,20 @@ query { * File will be created (unsaved) in operations/ folder, with an auto-generated named based on the schema type * Mutation will be generated with all * */ - async function schemaAddData(ast: ObjectTypeDefinitionNode) { + async function schemaAddData( + ast: ObjectTypeDefinitionNode, + documentPath: string, + ) { // generate content for the file const preamble = "# This is a file for you to write an un-named mutation. \n# Only one un-named mutation is allowed per file."; - const introspect = (await dataConnectService.introspect())?.data; - const schema = buildClientSchema(introspect!); + + const introspect = await dataConnectService.introspect(); + if (!introspect.data) { + vscode.window.showErrorMessage("Failed to generate mutation. Please check your compilation errors."); + return; + } + const schema = buildClientSchema(introspect.data); const dataType = schema.getType(`${ast.name.value}_Data`); if (!isInputObjectType(dataType)) return; @@ -152,8 +167,14 @@ query { ), ); const content = [preamble, adhocMutation].join("\n"); - const basePath = vscode.workspace.rootPath + "/dataconnect/"; - const filePath = vscode.Uri.file(`${basePath}${ast.name.value}_insert.gql`); + + // get root where dataconnect.yaml lives + const configs = await firstWhereDefined(dataConnectConfigs); + const dataconnectConfig = + configs.tryReadValue?.findEnclosingServiceForPath(documentPath); + const basePath = dataconnectConfig?.path; + + const filePath = vscode.Uri.file(`${basePath}/${ast.name.value}_insert.gql`); const doesFileExist = await checkIfFileExists(filePath); if (!doesFileExist) { @@ -202,7 +223,10 @@ query { selections: [ { kind: Kind.FIELD, - name: { kind: Kind.NAME, value: `${singularName.charAt(0).toLowerCase()}${singularName.slice(1)}_insert` }, + name: { + kind: Kind.NAME, + value: `${singularName.charAt(0).toLowerCase()}${singularName.slice(1)}_insert`, + }, arguments: [ { kind: Kind.ARGUMENT, @@ -251,16 +275,16 @@ query { return Disposable.from( vscode.commands.registerCommand( "firebase.dataConnect.schemaAddData", - (ast) => { + (ast, uri) => { telemetryLogger.logUsage(DATA_CONNECT_EVENT_NAME.ADD_DATA); - schemaAddData(ast); + schemaAddData(ast, uri); }, ), vscode.commands.registerCommand( "firebase.dataConnect.schemaReadData", - (document, ast) => { + (document, ast, uri) => { telemetryLogger.logUsage(DATA_CONNECT_EVENT_NAME.READ_DATA); - schemaReadData(document, ast); + schemaReadData(document, ast, uri); }, ), ); diff --git a/firebase-vscode/src/data-connect/code-lens-provider.ts b/firebase-vscode/src/data-connect/code-lens-provider.ts index 331cab7affc..51679fd0d43 100644 --- a/firebase-vscode/src/data-connect/code-lens-provider.ts +++ b/firebase-vscode/src/data-connect/code-lens-provider.ts @@ -150,18 +150,14 @@ export class SchemaCodeLensProvider extends ComputedCodeLensProvider { if (x.kind === Kind.OBJECT_TYPE_DEFINITION && x.loc) { const line = x.loc.startToken.line - 1; const range = new vscode.Range(line, 0, line, 0); - const position = new vscode.Position(line, 0); - const schemaLocation = { - documentPath: document.fileName, - position: position, - }; + const documentPath = document.fileName; codeLenses.push( new vscode.CodeLens(range, { title: `$(database) Add data`, command: "firebase.dataConnect.schemaAddData", tooltip: "Generate a mutation to add data of this type", - arguments: [x, schemaLocation], + arguments: [x, documentPath], }), ); @@ -170,7 +166,7 @@ export class SchemaCodeLensProvider extends ComputedCodeLensProvider { title: `$(database) Read data`, command: "firebase.dataConnect.schemaReadData", tooltip: "Generate a query to read data of this type", - arguments: [documentNode, x], + arguments: [documentNode, x, documentPath], }), ); } diff --git a/firebase-vscode/src/test/integration/fishfood/graphql.ts b/firebase-vscode/src/test/integration/fishfood/graphql.ts index d04fd3feeda..1097736e233 100644 --- a/firebase-vscode/src/test/integration/fishfood/graphql.ts +++ b/firebase-vscode/src/test/integration/fishfood/graphql.ts @@ -201,4 +201,89 @@ firebaseSuite("GraphQL", async function () { expect(editorTitle).toBe("Post_read.gql"); }, ); + + firebaseTest( + "Add Data should generate file in correct folder", + async function () { + const workbench = await browser.getWorkbench(); + + const sidebar = new FirebaseSidebar(workbench); + await sidebar.openExtensionSidebar(); + + const schemaFilePath = path.join( + __dirname, + "..", + "..", + "test_projects", + "fishfood", + "dataconnect", + "schema", + "schema.gql", + ); + + // Open the schema file + const editorView = new EditorView(workbench); + await editorView.openFile(schemaFilePath); + + // Verify that inline Add Data button is displayed + const addDataButton = await editorView.addDataButton; + await addDataButton.waitForDisplayed(); + + // Click the Add Data button + await addDataButton.click(); + + // Wait a bit for the mutation to be generated + await browser.pause(1500); + + // Verify the generated mutation file path + const activeEditor = await editorView.getActiveEditor(); + const filePath = activeEditor?.document.fileName; + expect(filePath).toContain( + "test_projects/fishfood/dataconnect/Post_insert.gql", + ); + + await editorView.closeCurrentEditor(); + }, + ); + + firebaseTest( + "Read Data should generate file in correct folder", + async function () { + const workbench = await browser.getWorkbench(); + + const sidebar = new FirebaseSidebar(workbench); + await sidebar.openExtensionSidebar(); + + const schemaFilePath = path.join( + __dirname, + "..", + "..", + "test_projects", + "fishfood", + "dataconnect", + "schema", + "schema.gql", + ); + + // Open the schema file + const editorView = new EditorView(workbench); + await editorView.openFile(schemaFilePath); + + // Verify that inline Read Data button is displayed + const readDataButton = await editorView.readDataButton; + await readDataButton.waitForDisplayed(); + + // Click the Read Data button + await readDataButton.click(); + + // Wait a bit for the query to be generated + await browser.pause(1500); + + // Verify the generated query file path + const activeEditor = await editorView.getActiveEditor(); + const filePath = activeEditor?.document.fileName; + expect(filePath).toContain("test_projects/fishfood/dataconnect/Post_read.gql"); + await editorView.closeCurrentEditor(); + }, + ); });