Skip to content

Commit

Permalink
Allow cloud to provide the known server version before introspection …
Browse files Browse the repository at this point in the history
…+ hide sql mode in editor and repl if server does not support it
  • Loading branch information
jaclarke committed Dec 6, 2024
1 parent d19194b commit 863322a
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 43 deletions.
2 changes: 1 addition & 1 deletion shared/common/components/infoCards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function InfoCards({
className?: string;
listClassName?: string;
extraCards?: (InfoCardDef | null)[];
currentVersion?: {major: number; minor: number; stage: string} | null;
currentVersion?: {major: number; minor: number} | null;
}) {
const data = useLatestInfo();

Expand Down
11 changes: 8 additions & 3 deletions shared/studio/state/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
prop,
_async,
_await,
Frozen,
} from "mobx-keystone";

import {AuthenticationError} from "edgedb";
Expand Down Expand Up @@ -118,11 +119,15 @@ export function createAuthenticatedFetch({

@model("Connection")
export class Connection extends Model({
config: prop<ConnectConfig>(),
config: prop<Frozen<ConnectConfig>>(),
serverVersion: prop<Frozen<{major: number; minor: number} | null>>(),
}) {
conn = AdminUIFetchConnection.create(
createAuthenticatedFetch(this.config),
codecsRegistry
createAuthenticatedFetch(this.config.data),
codecsRegistry,
this.serverVersion.data
? [this.serverVersion.data.major, this.serverVersion.data.minor]
: undefined
);

private _runningQuery = false;
Expand Down
6 changes: 4 additions & 2 deletions shared/studio/state/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
prop,
_async,
_await,
frozen,
} from "mobx-keystone";

import {
Expand Down Expand Up @@ -148,12 +149,13 @@ export class DatabaseState extends Model({
if (this.currentRole) {
this.setConnection(
new Connection({
config: {
config: frozen({
serverUrl: instanceState.serverUrl,
authToken: instanceState.authToken!,
database: this.name,
user: this.currentRole,
},
}),
serverVersion: frozen(instanceState.serverVersion),
})
);
}
Expand Down
41 changes: 27 additions & 14 deletions shared/studio/state/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ModelCreationData,
getTypeInfo,
ModelTypeInfo,
frozen,
} from "mobx-keystone";

import {AuthenticationError} from "edgedb";
Expand All @@ -29,9 +30,11 @@ import {Connection, createAuthenticatedFetch} from "./connection";
export const instanceCtx = createMobxContext<InstanceState>();

export async function createInstanceState(
props: ModelCreationData<InstanceState>
props: ModelCreationData<InstanceState>,
serverVersion: {major: number; minor: number}
) {
const instance = new InstanceState(props);
runInAction(() => (instance.serverVersion = serverVersion));

await instance.fetchInstanceInfo();

Expand All @@ -41,9 +44,6 @@ export async function createInstanceState(
export interface ServerVersion {
major: number;
minor: number;
stage: string;
stage_no: number;
local: string[];
}

@model("InstanceState")
Expand All @@ -56,7 +56,7 @@ export class InstanceState extends Model({
databasePageStates: prop(() => objectMap<DatabaseState>()),
}) {
@observable instanceName: string | null = null;
@observable serverVersion: ServerVersion | null = null;
@observable.ref serverVersion: ServerVersion | null = null;
@observable databases: string[] | null = null;
@observable roles: string[] | null = null;

Expand All @@ -77,7 +77,10 @@ export class InstanceState extends Model({
user: this.authUsername ?? "edgedb",
authToken: this.authToken!,
}),
codecsRegistry
codecsRegistry,
this.serverVersion
? [this.serverVersion.major, this.serverVersion.minor]
: undefined
);
try {
const data = (
Expand All @@ -98,7 +101,8 @@ export class InstanceState extends Model({

runInAction(() => {
this.instanceName = data.instanceName ?? "_localdev";
(this.serverVersion = data.version), (this.databases = data.databases);
this.serverVersion = data.version;
this.databases = data.databases;
this.roles = data.roles;
});

Expand All @@ -122,18 +126,25 @@ export class InstanceState extends Model({
(this.databases?.length ?? 0) > 0,
() => {
this.defaultConnection = new Connection({
config: {
config: frozen({
serverUrl: this.serverUrl,
authToken: this.authToken!,
database: this.databases![0],
user: this.authUsername ?? this.roles![0],
},
}),
serverVersion: frozen(this.serverVersion),
});
}
);

return reaction(
() => [this.serverUrl, this.authToken, this.authUsername, this.roles],
() => [
this.serverUrl,
this.authToken,
this.authUsername,
this.roles,
this.serverVersion,
],
() => (this._connections = new Map())
);
}
Expand All @@ -143,12 +154,13 @@ export class InstanceState extends Model({
let conn = this._connections.get(dbName);
if (!conn) {
conn = new Connection({
config: {
config: frozen({
serverUrl: this.serverUrl,
authToken: this.authToken!,
database: dbName,
user: this.authUsername ?? this.roles![0],
},
}),
serverVersion: frozen(this.serverVersion),
});
this._connections.set(dbName, conn);
}
Expand Down Expand Up @@ -195,12 +207,13 @@ export class InstanceState extends Model({
const schemaScript = await exampleSchema;
await this.defaultConnection!.execute(`create database _example`);
const exampleConn = new Connection({
config: {
config: frozen({
serverUrl: this.serverUrl,
authToken: this.authToken!,
database: "_example",
user: this.authUsername ?? this.roles![0],
},
}),
serverVersion: frozen(this.serverVersion),
});
await exampleConn.execute(schemaScript);
await this.fetchInstanceInfo();
Expand Down
2 changes: 1 addition & 1 deletion shared/studio/tabs/ai/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ export class AIAdminState extends Model({
try {
const stream: SSEStream = yield* _await(
runRAGQuery(
connectConfig,
connectConfig.data,
request,
this._runningPlaygroundAbortController
)
Expand Down
20 changes: 11 additions & 9 deletions shared/studio/tabs/queryEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,17 @@ export const QueryEditorView = observer(function QueryEditorView() {
<span>EdgeQL Builder</span>
{/* <BuilderTabIcon /> */}
</div>
<div
className={cn(styles.tab, {
[styles.selected]:
editorState.selectedEditor === EditorKind.SQL,
})}
onClick={() => editorState.setSelectedEditor(EditorKind.SQL)}
>
<span>SQL</span>
</div>
{editorState.sqlModeSupported ? (
<div
className={cn(styles.tab, {
[styles.selected]:
editorState.selectedEditor === EditorKind.SQL,
})}
onClick={() => editorState.setSelectedEditor(EditorKind.SQL)}
>
<span>SQL</span>
</div>
) : null}
</div>

<div className={styles.controls}>
Expand Down
16 changes: 15 additions & 1 deletion shared/studio/tabs/queryEditor/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export class QueryEditor extends Model({
),
_sqlParamsEditor: prop(() => new QueryParamsEditor({lang: Language.SQL})),

selectedEditor: prop<EditorKind>(EditorKind.EdgeQL).withSetter(),
selectedEditor: prop<EditorKind>(EditorKind.EdgeQL),

showHistory: prop(false),
queryHistory: prop<QueryHistoryItem[]>(() => [null as any]),
Expand All @@ -196,6 +196,20 @@ export class QueryEditor extends Model({
return this.runningQueryAbort != null;
}

@computed
get sqlModeSupported(): boolean {
const serverVersion = instanceCtx.get(this)!.serverVersion;
return !serverVersion || serverVersion.major >= 6;
}

@modelAction
setSelectedEditor(kind: EditorKind) {
if (kind === EditorKind.SQL && !this.sqlModeSupported) {
return;
}
this.selectedEditor = kind;
}

@observable.shallow
currentQueryData: QueryData = {
[EditorKind.EdgeQL]: Text.empty,
Expand Down
21 changes: 13 additions & 8 deletions shared/studio/tabs/repl/commands.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {CommandOutputKind, CommandResult} from "./state/commands";

import styles from "./repl.module.scss";
import {Repl} from "./state";

export function renderCommandResult(result: CommandResult) {
export function renderCommandResult(state: Repl, result: CommandResult) {
const ctrlKey = navigator.platform.toLowerCase().includes("mac")
? "Cmd"
: "Ctrl";
Expand All @@ -17,13 +18,17 @@ export function renderCommandResult(result: CommandResult) {
<div className={styles.command}>{ctrlKey}+ArrowUp/ArrowDown</div>
<div className={styles.info}>Navigate query history</div>

<div className={styles.heading}>Settings</div>
<div className={styles.command}>
\set language {"("}edgeql | sql{")"},
<br />
\edgeql, \sql
</div>
<div className={styles.info}>Set the query language</div>
{state.sqlModeSupported ? (
<>
<div className={styles.heading}>Settings</div>
<div className={styles.command}>
\set language {"("}edgeql | sql{")"},
<br />
\edgeql, \sql
</div>
<div className={styles.info}>Set the query language</div>
</>
) : null}

<div className={styles.heading}>Introspection</div>
<div className={styles.subheading}>
Expand Down
2 changes: 1 addition & 1 deletion shared/studio/tabs/repl/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ const ReplHistoryItem = observer(function ReplHistoryItem({
output = <div className={styles.queryStatus}>OK: {item.status}</div>;
}
} else if (item.commandResult) {
output = renderCommandResult(item.commandResult.data);
output = renderCommandResult(state, item.commandResult.data);
} else {
output = (
<>
Expand Down
11 changes: 9 additions & 2 deletions shared/studio/tabs/repl/state/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,15 @@ export async function handleSlashCommand(
}
case "edgeql":
case "sql": {
item.setCommandResult({kind: CommandOutputKind.none});
repl.setLanguage(command === "sql" ? ReplLang.SQL : ReplLang.EdgeQL);
if (command === "edgeql" || repl.sqlModeSupported) {
item.setCommandResult({kind: CommandOutputKind.none});
repl.setLanguage(command === "sql" ? ReplLang.SQL : ReplLang.EdgeQL);
} else {
item.setCommandResult({
kind: CommandOutputKind.error,
msg: `This version of Gel does not support SQL mode`,
});
}
break;
}
case "clear": {
Expand Down
9 changes: 9 additions & 0 deletions shared/studio/tabs/repl/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,20 @@ export class Repl extends Model({

scrollRef: HTMLDivElement | null = null;

@computed
get sqlModeSupported(): boolean {
const serverVersion = instanceCtx.get(this)!.serverVersion;
return !serverVersion || serverVersion.major >= 6;
}

@observable
language = ReplLang.EdgeQL;

@action
setLanguage(lang: ReplLang) {
if (lang === ReplLang.SQL && !this.sqlModeSupported) {
return;
}
this.language = lang;
}

Expand Down
7 changes: 6 additions & 1 deletion web/src/state/models/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ export const appCtx = createContext<App>();
@model("App")
export class App extends Model({
instanceState: prop(
() => new InstanceState({serverUrl, authToken, authUsername})
() =>
new InstanceState({
serverUrl,
authToken,
authUsername,
})
),
}) {
onInit() {
Expand Down

0 comments on commit 863322a

Please sign in to comment.