diff --git a/firebase-vscode/CHANGELOG.md b/firebase-vscode/CHANGELOG.md index 433b61787ce..c0a0ed3a0ce 100644 --- a/firebase-vscode/CHANGELOG.md +++ b/firebase-vscode/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## 0.2.0 + +- Fix Auth on IDX + +## 0.1.9 + +- Fix "Add Data" for nonnull and custom keys +- Emulator Bump 1.1.17 + ## 0.1.8 - Update Extensions page Logo diff --git a/firebase-vscode/common/messaging/protocol.ts b/firebase-vscode/common/messaging/protocol.ts index 837e81db46b..c6cd978cb67 100644 --- a/firebase-vscode/common/messaging/protocol.ts +++ b/firebase-vscode/common/messaging/protocol.ts @@ -113,6 +113,9 @@ export interface WebviewToExtensionParamsMap { // Initialize "result" tab. getDataConnectResults: void; + + // execute terminal tasks + executeLogin: void; } export interface DataConnectResults { diff --git a/firebase-vscode/package-lock.json b/firebase-vscode/package-lock.json index b77275038d3..937089641f0 100644 --- a/firebase-vscode/package-lock.json +++ b/firebase-vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-vscode", - "version": "0.1.8", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "firebase-vscode", - "version": "0.1.8", + "version": "0.2.0", "dependencies": { "@preact/signals-core": "^1.4.0", "@preact/signals-react": "1.3.6", diff --git a/firebase-vscode/package.json b/firebase-vscode/package.json index 6376d3b52cb..c3521fb3796 100644 --- a/firebase-vscode/package.json +++ b/firebase-vscode/package.json @@ -4,7 +4,7 @@ "publisher": "firebase", "icon": "./resources/firebase_logo.png", "description": "VSCode Extension for Firebase", - "version": "0.1.8", + "version": "0.2.0", "engines": { "vscode": "^1.69.0" }, @@ -13,7 +13,9 @@ "categories": [ "Other" ], - "extensionDependencies": ["graphql.vscode-graphql-syntax"], + "extensionDependencies": [ + "graphql.vscode-graphql-syntax" + ], "activationEvents": [ "onStartupFinished", "onLanguage:graphql", @@ -55,12 +57,12 @@ "properties": { "firebase.debug": { "type": "boolean", - "default": false, + "default": true, "description": "Enable writing debug-level messages to the file provided in firebase.debugLogPath (requires restart)" }, "firebase.debugLogPath": { "type": "string", - "default": "", + "default": "/tmp/firebase-plugin.log", "description": "If firebase.debug is true, appends debug-level messages to the provided file (requires restart)" }, "firebase.npmPath": { diff --git a/firebase-vscode/scripts/swap-pkg.js b/firebase-vscode/scripts/swap-pkg.js index 8a2ad521664..4c428799793 100644 --- a/firebase-vscode/scripts/swap-pkg.js +++ b/firebase-vscode/scripts/swap-pkg.js @@ -2,42 +2,10 @@ const { writeFileSync } = require("fs"); const path = require("path"); const pkg = require(path.join(__dirname, "../package.json")); -// Swaps package.json config as appropriate for packaging for -// Monospace or VSCE marketplace. -// TODO(chholland): Don't overwrite the real package.json file and -// create a generated one in dist/ - redo .vscodeignore to package -// dist/ - -let target = "vsce"; - -process.argv.forEach((arg) => { - if (arg === "vsce" || arg === "monospace") { - target = arg; - } -}); - -if (target === "vsce") { - delete pkg.extensionDependencies; - console.log( - "Removing google.monospace extensionDependency for VSCE packaging." - ); - pkg.contributes.configuration.properties["firebase.debug"].default = false; - pkg.contributes.configuration.properties["firebase.debugLogPath"].default = - ""; - console.log("Setting default debug log settings to off for VSCE packaging."); -} else if (target === "monospace") { - pkg.extensionDependencies = ["google.monospace"]; - console.log( - "Adding google.monospace extensionDependency for Monospace packaging." - ); - pkg.contributes.configuration.properties["firebase.debug"].default = true; - pkg.contributes.configuration.properties["firebase.debugLogPath"].default = - "/tmp/firebase-plugin.log"; - console.log( - "Setting default debug log settings to on for Monospace packaging." - ); -} +pkg.contributes.configuration.properties["firebase.debug"].default = true; +pkg.contributes.configuration.properties["firebase.debugLogPath"].default = + "/tmp/firebase-plugin.log"; writeFileSync( path.join(__dirname, "../package.json"), diff --git a/firebase-vscode/src/cli.ts b/firebase-vscode/src/cli.ts index 3a008fed6cc..61bb4810c5c 100644 --- a/firebase-vscode/src/cli.ts +++ b/firebase-vscode/src/cli.ts @@ -100,9 +100,9 @@ async function getServiceAccount() { */ async function requireAuthWrapper(showError: boolean = true): Promise { // Try to get global default from configstore. For some reason this is - // often overwritten when restarting the extension. pluginLogger.debug("requireAuthWrapper"); let account = getGlobalDefaultAccount(); + // often overwritten when restarting the extension. if (!account) { // If nothing in configstore top level, grab the first "additionalAccount" const accounts = getAllAccounts(); @@ -153,7 +153,7 @@ async function requireAuthWrapper(showError: boolean = true): Promise { // "error". Usually set on user-triggered actions such as // init hosting and deploy. pluginLogger.error( - `requireAuth error: ${e.original?.message || e.message}` + `requireAuth error: ${e.original?.message || e.message}`, ); vscode.window.showErrorMessage("Not logged in", { modal: true, @@ -164,7 +164,7 @@ async function requireAuthWrapper(showError: boolean = true): Promise { // but we should log it for debugging purposes. pluginLogger.debug( "requireAuth error output: ", - e.original?.message || e.message + e.original?.message || e.message, ); } return false; diff --git a/firebase-vscode/src/core/project.ts b/firebase-vscode/src/core/project.ts index d14c5fe7294..6d9f65977e5 100644 --- a/firebase-vscode/src/core/project.ts +++ b/firebase-vscode/src/core/project.ts @@ -6,14 +6,13 @@ import { FirebaseProjectMetadata } from "../types/project"; import { currentUser, isServiceAccount } from "./user"; import { listProjects } from "../cli"; import { pluginLogger } from "../logger-wrapper"; -import { selectProjectInMonospace } from "../../../src/monospace"; import { currentOptions } from "../options"; import { globalSignal } from "../utils/globals"; import { firstWhereDefined } from "../utils/signal"; /** Available projects */ export const projects = globalSignal>( - {} + {}, ); /** Currently selected project ID */ @@ -22,7 +21,7 @@ export const currentProjectId = globalSignal(""); const userScopedProjects = computed( () => { return projects.value[currentUser.value?.email ?? ""]; - } + }, ); /** Gets the currently selected project, fallback to first default project in RC file */ @@ -41,7 +40,7 @@ export const currentProject = computed( } return userScopedProjects.value?.find((p) => p.projectId === wantProjectId); - } + }, ); export function registerProject(broker: ExtensionBrokerImpl): Disposable { @@ -87,32 +86,7 @@ export function registerProject(broker: ExtensionBrokerImpl): Disposable { const command = vscode.commands.registerCommand( "firebase.selectProject", async () => { - if (process.env.MONOSPACE_ENV) { - pluginLogger.debug( - "selectProject: found MONOSPACE_ENV, " + - "prompting user using external flow" - ); - /** - * Monospace case: use Monospace flow - */ - const monospaceExtension = - vscode.extensions.getExtension("google.monospace"); - process.env.MONOSPACE_DAEMON_PORT = - monospaceExtension.exports.getMonospaceDaemonPort(); - try { - const projectId = await selectProjectInMonospace({ - projectRoot: currentOptions.value.cwd, - project: undefined, - isVSCE: true, - }); - - if (projectId) { - currentProjectId.value = projectId; - } - } catch (e) { - pluginLogger.error(e); - } - } else if (isServiceAccount.value) { + if (isServiceAccount.value) { return; } else { try { @@ -124,11 +98,11 @@ export function registerProject(broker: ExtensionBrokerImpl): Disposable { vscode.window.showErrorMessage(e.message); } } - } + }, ); const sub6 = broker.on("selectProject", () => - vscode.commands.executeCommand("firebase.selectProject") + vscode.commands.executeCommand("firebase.selectProject"), ); return vscode.Disposable.from( @@ -138,7 +112,7 @@ export function registerProject(broker: ExtensionBrokerImpl): Disposable { { dispose: sub3 }, { dispose: sub4 }, { dispose: sub5 }, - { dispose: sub6 } + { dispose: sub6 }, ); } @@ -149,7 +123,7 @@ export function registerProject(broker: ExtensionBrokerImpl): Disposable { */ export async function _promptUserForProject( projects: Thenable, - token?: vscode.CancellationToken + token?: vscode.CancellationToken, ): Promise { const items = projects.then((projects) => { return projects.map((p) => ({ diff --git a/firebase-vscode/src/core/user.ts b/firebase-vscode/src/core/user.ts index ad4c1773981..4c5f2349e07 100644 --- a/firebase-vscode/src/core/user.ts +++ b/firebase-vscode/src/core/user.ts @@ -23,7 +23,16 @@ export const isServiceAccount = computed(() => { return (currentUser.value as ServiceAccountUser)?.type === "service_account"; }); +export async function checkLogin() { + const accounts = await getAccounts(); + users.value = accounts.reduce( + (cumm, curr) => ({ ...cumm, [curr.user.email]: curr.user }), + {} + ); +} + export function registerUser(broker: ExtensionBrokerImpl): Disposable { + const sub1 = effect(() => { broker.send("notifyUsers", { users: Object.values(users.value) }); }); @@ -33,11 +42,7 @@ export function registerUser(broker: ExtensionBrokerImpl): Disposable { }); const sub3 = broker.on("getInitialData", async () => { - const accounts = await getAccounts(); - users.value = accounts.reduce( - (cumm, curr) => ({ ...cumm, [curr.user.email]: curr.user }), - {} - ); + checkLogin(); }); const sub4 = broker.on("addUser", async () => { diff --git a/firebase-vscode/src/data-connect/ad-hoc-mutations.ts b/firebase-vscode/src/data-connect/ad-hoc-mutations.ts index 519d13cd297..6c7663c47cc 100644 --- a/firebase-vscode/src/data-connect/ad-hoc-mutations.ts +++ b/firebase-vscode/src/data-connect/ad-hoc-mutations.ts @@ -1,8 +1,9 @@ import vscode, { Disposable } from "vscode"; -import { DocumentNode, Kind, ObjectTypeDefinitionNode } from "graphql"; +import { DocumentNode, GraphQLInputObjectType, GraphQLScalarType, Kind, ObjectTypeDefinitionNode, buildClientSchema, buildSchema } from "graphql"; import { checkIfFileExists, upsertFile } from "./file-utils"; +import { DataConnectService } from "./service"; -export function registerAdHoc(): Disposable { +export function registerAdHoc(dataConnectService: DataConnectService): Disposable { const defaultScalarValues = { Any: "{}", AuthUID: '""', @@ -122,7 +123,7 @@ query { // 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 adhocMutation = generateMutation(ast); + const adhocMutation = await generateMutation(ast); const content = [preamble, adhocMutation].join("\n"); const basePath = vscode.workspace.rootPath + "/dataconnect/"; @@ -149,33 +150,35 @@ query { } } - function generateMutation(ast: ObjectTypeDefinitionNode): string { - const name = + async function generateMutation( + ast: ObjectTypeDefinitionNode, + ): Promise { + const introspect = (await dataConnectService.introspect())?.data; + const schema = buildClientSchema(introspect); + + const name = ast.name.value; + const lowerCaseName = ast.name.value.charAt(0).toLowerCase() + ast.name.value.slice(1); + const dataName = `${name}_Data`; + const mutationDataType: GraphQLInputObjectType = schema.getTypeMap()[dataName] as GraphQLInputObjectType; + + // build mutation as string const functionSpacing = "\t"; const fieldSpacing = "\t\t"; const mutation = []; - mutation.push("mutation {"); // mutation header - mutation.push(`${functionSpacing}${name}_insert(data: {`); // insert function - for (const field of ast.fields) { + mutation.push(`${functionSpacing}${lowerCaseName}_insert(data: {`); + for (const [fieldName, field] of Object.entries(mutationDataType.getFields())) { // necessary to avoid type error - let fieldType: any = field.type; - // We unwrap NonNullType to obtain the actual type - if (fieldType.kind === Kind.NON_NULL_TYPE) { - fieldType = fieldType.type; - } - let fieldTypeName: string = fieldType.name.value; - let fieldName: string = field.name.value; - let defaultValue = defaultScalarValues[fieldTypeName] as string; - if (!isDataConnectScalarType(fieldTypeName)) { - fieldTypeName += "Id"; - fieldName += "Id"; - defaultValue = '""'; + const fieldtype: any = field.type; + // use all argument types that are of scalar, except x_expr + if (isDataConnectScalarType(fieldtype.name) && !field.name.includes("_expr")) { + const defaultValue = defaultScalarValues[fieldtype.name] || ""; + mutation.push( + `${fieldSpacing}${fieldName}: ${defaultValue} # ${fieldtype.name}`, + ); // field name + temp value + comment } - mutation.push( - `${fieldSpacing}${fieldName}: ${defaultValue} # ${fieldTypeName}`, - ); // field name + temp value + comment + } mutation.push(`${functionSpacing}})`, "}"); // closing braces/paren return mutation.join("\n"); diff --git a/firebase-vscode/src/data-connect/index.ts b/firebase-vscode/src/data-connect/index.ts index f3fdf8febd0..3e55cb2b2c5 100644 --- a/firebase-vscode/src/data-connect/index.ts +++ b/firebase-vscode/src/data-connect/index.ts @@ -28,6 +28,7 @@ import { runDataConnectCompiler } from "./core-compiler"; import { Result } from "../result"; import { runEmulatorIssuesStream } from "./emulator-stream"; import { LanguageClient } from "vscode-languageclient/node"; +import { registerTerminalTasks } from "./terminal"; class CodeActionsProvider implements vscode.CodeActionProvider { constructor( @@ -220,9 +221,10 @@ export function registerFdc( registerExecution(context, broker, fdcService, emulatorController), registerExplorer(context, broker, fdcService), registerFirebaseDataConnectView(context, broker, emulatorController), - registerAdHoc(), + registerAdHoc(fdcService), registerConnectors(context, broker, fdcService), registerFdcDeploy(broker), + registerTerminalTasks(broker), operationCodeLensProvider, vscode.languages.registerCodeLensProvider( // **Hack**: For testing purposes, enable code lenses on all graphql files diff --git a/firebase-vscode/src/data-connect/terminal.ts b/firebase-vscode/src/data-connect/terminal.ts index 2411042d2af..b45adcc7d8b 100644 --- a/firebase-vscode/src/data-connect/terminal.ts +++ b/firebase-vscode/src/data-connect/terminal.ts @@ -1,6 +1,7 @@ import { TerminalOptions } from "vscode"; -import * as vscode from "vscode"; - +import { ExtensionBrokerImpl } from "../extension-broker"; +import vscode, { Disposable } from "vscode"; +import { checkLogin } from "../core/user"; const environmentVariables = {}; const terminalOptions: TerminalOptions = { @@ -17,3 +18,52 @@ export function runCommand(command: string) { terminal.show(); terminal.sendText(command); } + +export function runTerminalTask( + taskName: string, + command: string, +): Promise { + const type = "firebase-" + Date.now(); + return new Promise(async (resolve, reject) => { + vscode.tasks.onDidEndTaskProcess(async (e) => { + if (e.execution.task.definition.type === type) { + e.execution.terminate(); + + if (e.exitCode === 0) { + resolve(`Successfully executed ${taskName} with command: ${command}`); + } else { + reject( + new Error(`Failed to execute ${taskName} with command: ${command}`), + ); + } + } + }); + vscode.tasks.executeTask( + new vscode.Task( + { type }, + vscode.TaskScope.Workspace, + taskName, + "firebase", + new vscode.ShellExecution(command), + ), + ); + }); +} + +export function registerTerminalTasks(broker: ExtensionBrokerImpl): Disposable { + const loginTaskBroker = broker.on("executeLogin", () => { + runTerminalTask("firebase login", "firebase login --no-localhost").then(() => { + checkLogin(); + }); + }); + + return Disposable.from( + { dispose: loginTaskBroker }, + vscode.commands.registerCommand( + "firebase.dataConnect.runTerminalTask", + (taskName, command) => { + runTerminalTask(taskName, command); + }, + ), + ); +} diff --git a/firebase-vscode/webviews/SidebarApp.tsx b/firebase-vscode/webviews/SidebarApp.tsx index 55704eb5a6c..2351820aeaf 100644 --- a/firebase-vscode/webviews/SidebarApp.tsx +++ b/firebase-vscode/webviews/SidebarApp.tsx @@ -13,6 +13,7 @@ import { ValueOrError } from "./messaging/protocol"; import { FirebaseConfig } from "../../src/firebaseConfig"; import { RCData } from "../../src/rc"; import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; +import { ServiceAccountUser } from "../common/types"; export function SidebarApp() { const env = useBroker("notifyEnv")?.env; @@ -158,9 +159,10 @@ function SidebarContent(props: { {accountSection} {!!user && ( - + )} - {hostingInitState === "success" && + { // TODO: disable hosting completely + /* {hostingInitState === "success" && !!user && !!projectId && env?.isMonospace && ( @@ -184,7 +186,7 @@ function SidebarContent(props: { hostingInitState={hostingInitState} setHostingInitState={setHostingInitState} /> - )} + )} */} { // disable emulator panel for now, as we have an individual emulator panel in the FDC section } diff --git a/firebase-vscode/webviews/components/AccountSection.tsx b/firebase-vscode/webviews/components/AccountSection.tsx index 79726e1061a..82f798d7ba0 100644 --- a/firebase-vscode/webviews/components/AccountSection.tsx +++ b/firebase-vscode/webviews/components/AccountSection.tsx @@ -34,9 +34,11 @@ export function AccountSection({ if (usersLoaded && (!allUsers.length || !user)) { // Users loaded but no user was found if (isMonospace) { - // Monospace: this is an error, should have found a workspace - // service account - currentUserElement = TEXT.MONOSPACE_LOGIN_FAIL; + currentUserElement = ( + broker.send("executeLogin")}> + {TEXT.GOOGLE_SIGN_IN} + + ); } else { // VS Code: prompt user to log in with Google account currentUserElement = ( @@ -49,7 +51,11 @@ export function AccountSection({ // Users loaded, at least one user was found if (user.type === "service_account") { if (isMonospace) { - currentUserElement = TEXT.MONOSPACE_LOGGED_IN; + currentUserElement = ( + broker.send("executeLogin")}> + {TEXT.GOOGLE_SIGN_IN} + + ); } else { currentUserElement = TEXT.VSCE_SERVICE_ACCOUNT_LOGGED_IN; } @@ -63,13 +69,6 @@ export function AccountSection({ {currentUserElement} ); - if (user?.type === "service_account" && isMonospace) { - userBoxElement = ( - - ); - } return (
{userBoxElement} diff --git a/firebase-vscode/webviews/components/ProjectSection.tsx b/firebase-vscode/webviews/components/ProjectSection.tsx index 34c6e4b3d30..5b4d01e3489 100644 --- a/firebase-vscode/webviews/components/ProjectSection.tsx +++ b/firebase-vscode/webviews/components/ProjectSection.tsx @@ -7,14 +7,26 @@ import React from "react"; import styles from "./AccountSection.scss"; import { ExternalLink } from "./ui/ExternalLink"; import { TEXT } from "../globals/ux-text"; +import { User } from "../types/auth"; +import { ServiceAccountUser } from "../types"; +interface UserWithType extends User { + type?: string; +} export function ProjectSection({ - userEmail, + user, projectId, + isMonospace, }: { - userEmail: string | null; + user: UserWithType | ServiceAccountUser | null; projectId: string | null | undefined; + isMonospace: boolean; }) { + const userEmail = user.email; + + if (isMonospace && user?.type === "service_account") { + return; + } return (