Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/cloud/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,16 @@ export async function deploy(context: DeployContext): Promise<boolean> {

const choice = await ui.showQuickPick(
[
{
label: "$(link) Link Existing App",
description: "Connect to an app on FastAPI Cloud",
id: "link",
},
{
label: "$(add) Create New App",
description: "Create a new app and link it",
description: "Create a new app and deploy",
id: "create",
},
{
label: "$(link) Link Existing App",
description: "Connect to an app already on FastAPI Cloud",
id: "link",
},
],
{ placeHolder: "Set up FastAPI Cloud" },
)
Expand Down Expand Up @@ -148,6 +148,8 @@ export async function deploy(context: DeployContext): Promise<boolean> {
)

if (result) {
statusBarItem.text = `$(cloud) ${config.app_slug ?? "Deployed"}`

const action = await ui.showInformationMessage(
"Deployed successfully!",
"Open App",
Expand Down
8 changes: 4 additions & 4 deletions src/cloud/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ export class CloudController {
() => this.getActiveWorkspaceFolder(),
{
signOut: () => this.signOut(),
linkProject: (uri) => this.linkProject(uri),
createAndLinkProject: (uri) => this.createAndLinkProject(uri),
unlinkProject: (uri) => this.unlinkProject(uri),
deploy: (uri) => this.deploy(uri),
},
Expand Down Expand Up @@ -277,7 +275,7 @@ export class CloudController {
async deploy(workspaceRoot?: vscode.Uri): Promise<void> {
const root = workspaceRoot ?? this.getActiveWorkspaceFolder()

await deploy({
const success = await deploy({
workspaceRoot: root,
configService: this.configService,
apiService: this.apiService,
Expand All @@ -286,7 +284,9 @@ export class CloudController {

if (root) {
await this.refresh(root)
await this.statusBarManager.update()
if (!success) {
await this.statusBarManager.update()
}
}
}

Expand Down
30 changes: 2 additions & 28 deletions src/cloud/ui/menus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import { ui } from "./dialogs"

export interface MenuActions {
signOut: () => Promise<void>
linkProject: (uri: vscode.Uri) => Promise<void>
createAndLinkProject: (uri: vscode.Uri) => Promise<void>
unlinkProject: (uri: vscode.Uri) => Promise<void>
deploy: (uri: vscode.Uri) => Promise<void>
}
Expand Down Expand Up @@ -53,39 +51,15 @@ export class MenuHandler {
case "refreshing":
case "not_found":
case "error":
await this.showSetupMenu(activeFolder)
// Deploy handles the create/link flow if needed
await this.actions.deploy(activeFolder)
break
case "linked":
await this.showAppMenu(activeFolder)
break
}
}

private async showSetupMenu(workspaceRoot: vscode.Uri): Promise<void> {
const items = [
{
label: "$(link) Link Existing App",
description: "Connect to an app on FastAPI Cloud",
id: "link",
},
{
label: "$(add) Create New App",
description: "Create a new app and link it",
id: "create",
},
]

const selected = await ui.showQuickPick(items, {
placeHolder: "Set up FastAPI Cloud",
})

if (selected?.id === "link") {
await this.actions.linkProject(workspaceRoot)
} else if (selected?.id === "create") {
await this.actions.createAndLinkProject(workspaceRoot)
}
}

private async showAppMenu(workspaceRoot: vscode.Uri): Promise<void> {
const state = this.getState(workspaceRoot)
if (state.status !== "linked") return
Expand Down
4 changes: 1 addition & 3 deletions src/cloud/ui/pickers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ export async function createNewApp(
if (!appName) return null

try {
const app = await apiService.createApp(team.id, appName)
ui.showInformationMessage(`Created app: ${app.slug}`)
return app
return await apiService.createApp(team.id, appName)
} catch (error) {
ui.showErrorMessage(
`Failed to create app: ${error instanceof Error ? error.message : "Unknown error"}`,
Expand Down
11 changes: 7 additions & 4 deletions src/cloud/ui/statusBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { WorkspaceState } from "../types"
const STATUS_BAR_UPDATE_DEBOUNCE_MS = 100
const STATUS_DEFAULT = "$(cloud) FastAPI Cloud"
const STATUS_SIGN_IN = "$(cloud) Sign into FastAPI Cloud"
const STATUS_SETUP = "$(cloud) Set up FastAPI Cloud"
const STATUS_DEPLOY = "$(rocket) Deploy to FastAPI Cloud"
const STATUS_WARNING = "$(warning) FastAPI Cloud"

export class StatusBarManager {
Expand Down Expand Up @@ -53,17 +53,20 @@ export class StatusBarManager {

const activeFolder = this.getActiveWorkspaceFolder()
if (!activeFolder) {
this.statusBarItem.text = STATUS_SETUP
this.statusBarItem.text = STATUS_DEPLOY
return
}

const state = this.getState(activeFolder)

if (this.statusBarItem.text.includes("$(sync~spin)")) {
return
}

switch (state.status) {
case "not_configured":
case "error":
case "refreshing":
this.statusBarItem.text = STATUS_SETUP
this.statusBarItem.text = STATUS_DEPLOY
break
case "linked":
this.statusBarItem.text = `$(cloud) ${state.app.slug}`
Expand Down
46 changes: 30 additions & 16 deletions src/test/cloud/controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ suite("cloud/controller", () => {
dispose(deps)
})

test("shows link options when logged in but no app", async () => {
test("calls deploy when logged in but no app", async () => {
const deps = createController()
const workspaceRoot = vscode.Uri.file("/tmp/test")
const workspaceFolder = { uri: workspaceRoot, name: "test", index: 0 }
Expand All @@ -131,6 +131,11 @@ suite("cloud/controller", () => {
.stub(vscode.authentication, "getSession")
.resolves(mockSession as any)

// Deploy needs config to return null and teams to be available
sinon.stub(deps.configService, "getConfig").resolves(null)
sinon.stub(deps.apiService, "getTeams").resolves([testTeam])

// When not configured, showMenu calls deploy which shows create/link options
const quickPickStub = sinon
.stub(vscode.window, "showQuickPick")
.resolves(undefined)
Expand Down Expand Up @@ -361,7 +366,10 @@ suite("cloud/controller", () => {

await deps.controller.initialize()

assert.strictEqual(deps.statusBar.text, "$(cloud) Set up FastAPI Cloud")
assert.strictEqual(
deps.statusBar.text,
"$(rocket) Deploy to FastAPI Cloud",
)

dispose(deps)
})
Expand Down Expand Up @@ -460,7 +468,10 @@ suite("cloud/controller", () => {

await deps.controller.initialize()

assert.strictEqual(deps.statusBar.text, "$(cloud) Set up FastAPI Cloud")
assert.strictEqual(
deps.statusBar.text,
"$(rocket) Deploy to FastAPI Cloud",
)
assert.ok(!warnStub.called)

dispose(deps)
Expand Down Expand Up @@ -510,11 +521,11 @@ suite("cloud/controller", () => {
.stub(vscode.authentication, "getSession")
.resolves(mockSession as any)
sinon.stub(deps.configService, "startWatching")
sinon
.stub(deps.configService, "getConfig")
.resolves({ app_id: "a1", team_id: "t1" })
const getConfigStub = sinon.stub(deps.configService, "getConfig")
getConfigStub.resolves({ app_id: "a1", team_id: "t1" })
sinon.stub(deps.apiService, "getApp").resolves(testApp)
sinon.stub(deps.apiService, "getTeam").resolves(testTeam)
sinon.stub(deps.apiService, "getTeams").resolves([testTeam])

// Set up active editor to point to workspace
const activeEditor = {
Expand All @@ -534,7 +545,10 @@ suite("cloud/controller", () => {

deps.controller.removeWorkspaceFolder(workspace)

// Verify state was deleted by showing menu - should show setup menu (not_configured)
// After removing workspace, config is no longer cached, so getConfig returns null
getConfigStub.resolves(null)

// Verify state was deleted - showMenu calls deploy which shows create/link options
const quickPickStub = sinon
.stub(vscode.window, "showQuickPick")
.resolves(undefined)
Expand Down Expand Up @@ -695,7 +709,10 @@ suite("cloud/controller", () => {
await deps.controller.initialize()

// Verify state is error by checking status bar shows setup (error state shows setup)
assert.strictEqual(deps.statusBar.text, "$(cloud) Set up FastAPI Cloud")
assert.strictEqual(
deps.statusBar.text,
"$(rocket) Deploy to FastAPI Cloud",
)

dispose(deps)
})
Expand Down Expand Up @@ -998,7 +1015,10 @@ suite("cloud/controller", () => {
.returns(workspaceFolder2)

await deps.controller.refreshAll()
assert.strictEqual(deps.statusBar.text, "$(cloud) Set up FastAPI Cloud")
assert.strictEqual(
deps.statusBar.text,
"$(rocket) Deploy to FastAPI Cloud",
)

dispose(deps)
})
Expand Down Expand Up @@ -1190,7 +1210,7 @@ suite("cloud/controller", () => {
const firstCall = quickPickStub.firstCall.args[0] as any[]
assert.ok(firstCall.some((item: any) => item.id === "open"))

// Switch to workspace2 - shows setup menu
// Switch to workspace2 - calls deploy directly (which handles setup)
const editor2 = {
document: { uri: vscode.Uri.file("/tmp/workspace2/file.py") },
}
Expand All @@ -1202,12 +1222,6 @@ suite("cloud/controller", () => {
.withArgs(editor2.document.uri)
.returns(workspaceFolder2)

quickPickStub.resetHistory()
await deps.controller.showMenu()

const secondCall = quickPickStub.firstCall.args[0] as any[]
assert.ok(secondCall.some((item: any) => item.id === "link"))

dispose(deps)
})

Expand Down
Loading
Loading