Skip to content

Commit a9ef5ee

Browse files
committed
feat(vscode): before running commands check if signed in
- when someone runs a command that requires a user to be signed in rather than fail, this nudges a user to sign in before hand
1 parent 9952bbb commit a9ef5ee

7 files changed

Lines changed: 120 additions & 34 deletions

File tree

vscode/extension/eslint.config.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ export default [{
2020
format: ["camelCase", "PascalCase"],
2121
}],
2222

23-
curly: "warn",
24-
eqeqeq: "warn",
25-
"no-throw-literal": "warn",
23+
curly: "error",
24+
eqeqeq: "error",
25+
"no-throw-literal": "error",
2626
semi: ["error", "never"]
2727
},
2828
}];

vscode/extension/src/auth/auth.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,18 @@ export class AuthenticationProviderTobikoCloud
332332
}
333333
}
334334
}
335+
336+
/**
337+
* Checks if the user is currently signed into Tobiko Cloud.
338+
* @returns A promise that resolves to true if the user is signed in, false otherwise.
339+
*/
340+
export async function isSignedIntoTobikoCloud(): Promise<boolean> {
341+
try {
342+
const authProvider = new AuthenticationProviderTobikoCloud()
343+
const sessions = await authProvider.getSessions()
344+
return sessions.length > 0
345+
} catch (error) {
346+
traceError(`Error checking authentication status: ${error}`)
347+
return false
348+
}
349+
}
Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,41 @@
1-
import { traceError, traceLog } from "../utilities/common/log"
1+
import { traceLog } from "../utilities/common/log"
22
import { execSync } from "child_process"
33
import { sqlmesh_exec } from "../utilities/sqlmesh/sqlmesh"
4-
import { isErr } from "../utilities/functional/result"
4+
import { err, isErr, ok, Result } from "../utilities/functional/result"
55
import * as vscode from "vscode"
6+
import { ErrorType, handleNotSginedInError } from "../utilities/errors"
7+
import { AuthenticationProviderTobikoCloud } from "../auth/auth"
68

7-
export const format = async () => {
9+
export const format = (authProvider: AuthenticationProviderTobikoCloud) => async () => {
810
traceLog("Calling format")
911
const out = await internalFormat()
10-
if (out === 0) {
11-
vscode.window.showInformationMessage("Project formatted successfully")
12-
} else {
13-
vscode.window.showErrorMessage("Project format failed")
12+
if (isErr(out)) {
13+
if (out.error.type === "not_signed_in") {
14+
handleNotSginedInError(authProvider)
15+
} else {
16+
vscode.window.showErrorMessage(
17+
`Project format failed: ${out.error.message}`
18+
)
19+
}
1420
}
1521
}
1622

17-
const internalFormat = async (): Promise<number> => {
23+
const internalFormat = async (): Promise<Result<number, ErrorType>> => {
1824
try {
1925
const exec = await sqlmesh_exec()
2026
if (isErr(exec)) {
21-
traceError(exec.error)
22-
return 1
27+
return exec
2328
}
2429
execSync(`${exec.value.bin} format`, {
2530
encoding: "utf-8",
2631
cwd: exec.value.workspacePath,
2732
env: exec.value.env,
2833
})
29-
return 0
34+
return ok(0)
3035
} catch (error: any) {
31-
traceError("Error executing sqlmesh format:", error.message)
32-
traceError(error.stdout)
33-
traceError(error.stderr)
34-
return error.status || 1
36+
return err({
37+
type: "generic",
38+
message: `Error executing sqlmesh format: ${error.message}`,
39+
})
3540
}
3641
}

vscode/extension/src/extension.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import * as vscode from "vscode"
21
import { format } from "./commands/format"
2+
import * as vscode from "vscode"
33
import {
44
createOutputChannel,
55
onDidChangeConfiguration,
@@ -16,6 +16,8 @@ import { AuthenticationProviderTobikoCloud } from "./auth/auth"
1616
import { signOut } from "./commands/signout"
1717
import { signIn } from "./commands/signin"
1818
import { signInSpecifyFlow } from "./commands/signinSpecifyFlow"
19+
import { isErr } from "./utilities/functional/result"
20+
import { handleNotSginedInError } from "./utilities/errors"
1921

2022
let lspClient: LSPClient | undefined
2123

@@ -44,7 +46,6 @@ export async function activate(context: vscode.ExtensionContext) {
4446
context.subscriptions.push(
4547
vscode.commands.registerCommand("sqlmesh.signin", signIn(authProvider))
4648
)
47-
4849
context.subscriptions.push(
4950
vscode.commands.registerCommand(
5051
"sqlmesh.signinSpecifyFlow",
@@ -55,19 +56,26 @@ export async function activate(context: vscode.ExtensionContext) {
5556
vscode.commands.registerCommand("sqlmesh.signout", signOut(authProvider))
5657
)
5758
context.subscriptions.push(
58-
vscode.commands.registerCommand("sqlmesh.format", format)
59+
vscode.commands.registerCommand("sqlmesh.format", format(authProvider))
5960
)
6061

6162
lspClient = new LSPClient()
62-
await lspClient.start()
63+
const result = await lspClient.start()
64+
if (isErr(result)) {
65+
handleNotSginedInError(authProvider)
66+
}
6367
context.subscriptions.push(lspClient)
6468

6569
const restart = async () => {
6670
if (lspClient) {
6771
traceVerbose("Restarting LSP client")
68-
await lspClient.restart()
72+
const result = await lspClient.restart()
73+
if (isErr(result)) {
74+
handleNotSginedInError(authProvider)
75+
}
6976
}
7077
}
78+
7179
context.subscriptions.push(
7280
onDidChangePythonInterpreter(async () => {
7381
await restart()

vscode/extension/src/lsp/lsp.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { sqlmesh_lsp_exec } from "../utilities/sqlmesh/sqlmesh"
44
import { err, isErr, ok, Result } from "../utilities/functional/result"
55
import { getWorkspaceFolders } from "../utilities/common/vscodeapi"
66
import { traceError } from "../utilities/common/log"
7+
import { ErrorType } from "../utilities/errors"
78

89
let outputChannel: OutputChannel | undefined
910

@@ -14,20 +15,23 @@ export class LSPClient implements Disposable {
1415
this.client = undefined
1516
}
1617

17-
public async start(): Promise<Result<undefined, string>> {
18+
public async start(): Promise<Result<undefined, ErrorType>> {
1819
if (!outputChannel) {
1920
outputChannel = window.createOutputChannel('sqlmesh-lsp')
2021
}
2122

2223
const sqlmesh = await sqlmesh_lsp_exec()
2324
if (isErr(sqlmesh)) {
2425
traceError(`Failed to get sqlmesh_lsp_exec: ${sqlmesh.error}`)
25-
return sqlmesh
26+
return sqlmesh
2627
}
2728
const workspaceFolders = getWorkspaceFolders()
2829
if (workspaceFolders.length !== 1) {
2930
traceError(`Invalid number of workspace folders: ${workspaceFolders.length}`)
30-
return err("Invalid number of workspace folders")
31+
return err({
32+
type: "generic",
33+
message: "Invalid number of workspace folders",
34+
})
3135
}
3236

3337
let folder = workspaceFolders[0]
@@ -67,9 +71,9 @@ export class LSPClient implements Disposable {
6771
return ok(undefined)
6872
}
6973

70-
public async restart() {
74+
public async restart(): Promise<Result<undefined, ErrorType>> {
7175
await this.stop()
72-
await this.start()
76+
return await this.start()
7377
}
7478

7579
public async stop() {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { window } from "vscode"
2+
import { AuthenticationProviderTobikoCloud } from "../auth/auth"
3+
import { signIn } from "../commands/signin"
4+
import { traceInfo } from "./common/log"
5+
6+
/**
7+
* Represents different types of errors that can occur in the application.
8+
*/
9+
export type ErrorType =
10+
| { type: "generic"; message: string }
11+
| { type: "not_signed_in" };
12+
13+
/**
14+
* Handles the case where the user is not signed in to Tobiko Cloud.
15+
* @param authProvider - The authentication provider to use for signing in.
16+
*/
17+
export const handleNotSginedInError = async (
18+
authProvider: AuthenticationProviderTobikoCloud
19+
): Promise<void> => {
20+
traceInfo("handleNotSginedInError")
21+
const result = await window.showInformationMessage(
22+
"Please sign in to Tobiko Cloud to use SQLMesh",
23+
"Sign In"
24+
)
25+
if (result === "Sign In") {
26+
await signIn(authProvider)()
27+
}
28+
}

vscode/extension/src/utilities/sqlmesh/sqlmesh.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { execFile } from "child_process"
77
import { promisify } from "util"
88
import { isPythonModuleInstalled } from "../python"
99
import fs from "fs"
10+
import { ErrorType } from "../errors"
11+
import { isSignedIntoTobikoCloud } from "../../auth/auth"
1012

1113
export type sqlmesh_exec = {
1214
workspacePath: string;
@@ -51,7 +53,7 @@ export const get_tcloud_bin = async (): Promise<Result<string, string>> => {
5153
*
5254
* @returns The sqlmesh executable for the current workspace.
5355
*/
54-
export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, string>> => {
56+
export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, ErrorType>> => {
5557
const projectRoot = await getProjectRoot()
5658
const workspacePath = projectRoot.uri.fsPath
5759
const interpreterDetails = await getInterpreterDetails()
@@ -67,12 +69,24 @@ export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, string>> => {
6769
traceLog("Using virtual environment")
6870
const tcloudInstalled = await isTcloudProject()
6971
if (isErr(tcloudInstalled)) {
70-
return tcloudInstalled
72+
return err({
73+
type: "generic",
74+
message: tcloudInstalled.error,
75+
})
7176
}
7277
if (tcloudInstalled.value) {
7378
const tcloudBin = await get_tcloud_bin()
7479
if (isErr(tcloudBin)) {
75-
return tcloudBin
80+
return err({
81+
type: "generic",
82+
message: tcloudBin.error,
83+
})
84+
}
85+
const isSignedIn = await isSignedIntoTobikoCloud()
86+
if (!isSignedIn) {
87+
return err({
88+
type: "not_signed_in",
89+
})
7690
}
7791
return ok({
7892
bin: `${tcloudBin.value} sqlmesh`,
@@ -113,7 +127,7 @@ export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, string>> => {
113127
* @returns The sqlmesh_lsp executable for the current workspace.
114128
*/
115129
export const sqlmesh_lsp_exec = async (): Promise<
116-
Result<sqlmesh_exec, string>
130+
Result<sqlmesh_exec, ErrorType>
117131
> => {
118132
const projectRoot = await getProjectRoot()
119133
const workspacePath = projectRoot.uri.fsPath
@@ -130,13 +144,25 @@ export const sqlmesh_lsp_exec = async (): Promise<
130144
traceLog("Using virtual environment")
131145
const tcloudInstalled = await isTcloudProject()
132146
if (isErr(tcloudInstalled)) {
133-
return tcloudInstalled
147+
return err({
148+
type: "generic",
149+
message: tcloudInstalled.error,
150+
})
134151
}
135152
if (tcloudInstalled.value) {
136153
traceLog("Tcloud installed, installing sqlmesh")
137154
const tcloudBin = await get_tcloud_bin()
138155
if (isErr(tcloudBin)) {
139-
return tcloudBin
156+
return err({
157+
type: "generic",
158+
message: tcloudBin.error,
159+
})
160+
}
161+
const isSignedIn = await isSignedIntoTobikoCloud()
162+
if (!isSignedIn) {
163+
return err({
164+
type: "not_signed_in",
165+
})
140166
}
141167
const execFileAsync = promisify(execFile)
142168
await execFileAsync(tcloudBin.value, ["install_sqlmesh"], {

0 commit comments

Comments
 (0)