Skip to content

Commit

Permalink
Merge branch 'master' into add-table-script-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal authored Feb 10, 2024
2 parents 067421f + 28704bd commit d6677d2
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 62 deletions.
13 changes: 9 additions & 4 deletions src/app/(components)/ConnectionConfigScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { Textarea } from "@/components/ui/textarea";
import { validateConnectionEndpoint } from "@/lib/validation";
import { appVersion } from "@/env";
import ErrorMessage from "@/components/custom/ErrorMessage";

interface ConnectionItem {
id: string;
Expand Down Expand Up @@ -69,8 +70,8 @@ function ConnectionEdit({ onComplete }: { onComplete: () => void }) {

const onSaveClicked = () => {
// Validate the connection
const [isUrlInvalid, urlInvalidMessage] = validateConnectionEndpoint(url);
if (isUrlInvalid) {
const [isValid, urlInvalidMessage] = validateConnectionEndpoint(url);
if (!isValid) {
setError(urlInvalidMessage);
return;
}
Expand Down Expand Up @@ -173,11 +174,14 @@ export default function ConnectionConfigScreen() {
[router]
);

const onConnectClicked = () => {
const [valid, errorMessage] = validateConnectionEndpoint(url);

const onConnectClicked = useCallback(() => {
if (!valid) return;
if (url && token) {
connect(url, token);
}
};
}, [valid, connect, url, token]);

const onConnectionListChange = () => {
setConnections(getConnections());
Expand Down Expand Up @@ -256,6 +260,7 @@ export default function ConnectionConfigScreen() {
value={url}
onChange={(e) => setUrl(e.currentTarget.value)}
/>
{url && errorMessage && <ErrorMessage message={errorMessage} />}
</div>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="token">Token</Label>
Expand Down
12 changes: 10 additions & 2 deletions src/app/(components)/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { AutoCompleteProvider } from "@/context/AutoCompleteProvider";
import ContextMenuHandler from "./ContentMenuHandler";
import InternalPubSub from "@/lib/internal-pubsub";
import { useRouter } from "next/navigation";
import { normalizeConnectionEndpoint } from "@/lib/validation";
import { SchemaProvider } from "@/screens/DatabaseScreen/SchemaProvider";

function MainConnection({
credential,
Expand All @@ -29,7 +31,9 @@ function MainConnection({

return (
<DatabaseDriverProvider driver={database}>
<DatabaseGui />
<SchemaProvider>
<DatabaseGui />
</SchemaProvider>
</DatabaseDriverProvider>
);
}
Expand All @@ -47,7 +51,11 @@ function InvalidSession() {
export default function MainScreen() {
const router = useRouter();
const sessionCredential: { url: string; token: string } = useMemo(() => {
return JSON.parse(sessionStorage.getItem("connection") ?? "{}");
const config = JSON.parse(sessionStorage.getItem("connection") ?? "{}");
return {
url: normalizeConnectionEndpoint(config.url),
token: config.token,
};
}, []);

/**
Expand Down
26 changes: 23 additions & 3 deletions src/app/(components)/OptimizeTable/OptimizeTableState.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { selectArrayFromIndexList } from "@/lib/export-helper";
import { OptimizeTableHeaderProps } from ".";
import { OptimizeTableHeaderProps, TableColumnDataType } from ".";
import * as hrana from "@libsql/hrana-client";
import { DatabaseTableSchema } from "@/drivers/DatabaseDriver";
import { LucideKey } from "lucide-react";
Expand Down Expand Up @@ -33,11 +33,31 @@ export default class OptimizeTableState {
) {
return new OptimizeTableState(
dataResult.columnNames.map((headerName, idx) => {
let initialSize = 150;
const dataType = convertSqliteType(dataResult.columnDecltypes[idx]);

if (
dataType === TableColumnDataType.INTEGER ||
dataType === TableColumnDataType.REAL
) {
initialSize = 100;
} else if (dataType === TableColumnDataType.TEXT) {
// Use 100 first rows to determine the good initial size
let maxSize = 0;
for (let i = 0; i < Math.min(dataResult.rows.length, 100); i++) {
maxSize = Math.max(
(dataResult.rows[i][headerName ?? ""]?.toString() ?? "").length
);
}

initialSize = Math.max(150, Math.min(500, maxSize * 8));
}

return {
initialSize: 150,
initialSize,
name: headerName ?? "",
resizable: true,
dataType: convertSqliteType(dataResult.columnDecltypes[idx]),
dataType,
icon: schemaResult?.pk.includes(headerName ?? "") ? (
<LucideKey className="w-4 h-4 text-red-500" />
) : undefined,
Expand Down
18 changes: 9 additions & 9 deletions src/app/(components)/OptimizeTable/TableHeaderResizeHandler.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRef, useState, useEffect } from 'react';
import styles from './styles.module.css';
import { useRef, useState, useEffect } from "react";
import styles from "./styles.module.css";

export default function TableHeaderResizeHandler({
idx,
Expand Down Expand Up @@ -62,9 +62,9 @@ export default function TableHeaderResizeHandler({
onResize(idx, width);

if (table) {
const columns = table.style.gridTemplateColumns.split(' ');
columns[idx] = width + 'px';
table.style.gridTemplateColumns = columns.join(' ');
const columns = table.style.gridTemplateColumns.split(" ");
columns[idx] = width + "px";
table.style.gridTemplateColumns = columns.join(" ");
}

if (edgeResizing) {
Expand All @@ -76,12 +76,12 @@ export default function TableHeaderResizeHandler({
setResizing(false);
};

document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);

return () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
};
}
}
Expand Down
37 changes: 6 additions & 31 deletions src/app/(components)/SchemaView.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { buttonVariants } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import { useAutoComplete } from "@/context/AutoCompleteProvider";
import { useDatabaseDriver } from "@/context/DatabaseDriverProvider";
import { DatabaseSchemaItem } from "@/drivers/DatabaseDriver";
import { cn } from "@/lib/utils";
import { openTabs } from "@/messages/openTabs";
import { LucideIcon, Table2 } from "lucide-react";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useState } from "react";
import {
OpenContextMenuList,
openContextMenuFromEvent,
} from "@/messages/openContextMenu";
import OpacityLoading from "./OpacityLoading";
import { useSchema } from "@/screens/DatabaseScreen/SchemaProvider";

interface SchemaViewItemProps {
icon: LucideIcon;
Expand Down Expand Up @@ -59,26 +57,8 @@ function SchemaViewmItem({
}

export default function SchemaView() {
const { updateTableList } = useAutoComplete();
const [schemaItems, setSchemaItems] = useState<DatabaseSchemaItem[]>([]);
const [loading, setLoading] = useState(false);
const { refresh, schema } = useSchema();
const [selectedIndex, setSelectedIndex] = useState(-1);
const { databaseDriver } = useDatabaseDriver();

const fetchSchema = useCallback(() => {
setLoading(true);

databaseDriver.getTableList().then((tableList) => {
const sortedTableList = [...tableList];
sortedTableList.sort((a, b) => {
return a.name.localeCompare(b.name);
});

setSchemaItems(sortedTableList);
updateTableList(tableList.map((table) => table.name));
setLoading(false);
});
}, [databaseDriver, updateTableList]);

const prepareContextMenu = useCallback(
(tableName?: string) => {
Expand All @@ -91,24 +71,19 @@ export default function SchemaView() {
},
},
{ separator: true },
{ title: "Refresh", onClick: fetchSchema },
{ title: "Refresh", onClick: () => refresh() },
] as OpenContextMenuList;
},
[fetchSchema]
[refresh]
);

useEffect(() => {
fetchSchema();
}, [fetchSchema]);

return (
<ScrollArea
className="h-full select-none"
onContextMenu={openContextMenuFromEvent(prepareContextMenu())}
>
{loading && <OpacityLoading />}
<div className="flex flex-col p-2 pr-4">
{schemaItems.map((item, schemaIndex) => {
{schema.map((item, schemaIndex) => {
return (
<SchemaViewmItem
onContextMenu={openContextMenuFromEvent(
Expand Down
11 changes: 11 additions & 0 deletions src/app/storybook/connection_error/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";
import ConnectingDialog from "@/screens/DatabaseScreen/ConnectingDialog";

export default function ConnectionErrorMessageStory() {
return (
<div className="flex flex-col gap-4">
<ConnectingDialog message="Authentication failed: The JWT is invalid" />
<ConnectingDialog loading url="wss://example.turso.io" />
</div>
);
}
7 changes: 7 additions & 0 deletions src/components/custom/ErrorMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function ErrorMessage({
message,
}: {
readonly message: string;
}) {
return <div className="text-xs text-red-500">{message}</div>;
}
29 changes: 19 additions & 10 deletions src/drivers/DatabaseDriver.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ export interface DatabaseTableSchema {
export default class DatabaseDriver {
protected client: hrana.WsClient;
protected stream?: hrana.WsStream;
protected endpoint: string = "";

constructor(url: string, authToken: string) {
this.endpoint = url;
this.client = hrana.openWs(url, authToken);
}

Expand All @@ -89,6 +91,10 @@ export default class DatabaseDriver {
return this.stream;
}

getEndpoint() {
return this.endpoint;
}

async query(stmt: hrana.InStmt) {
const stream = this.getStream();

Expand Down Expand Up @@ -128,17 +134,20 @@ export default class DatabaseDriver {
}));

// Check auto increment
const seqCount = await this.query(
`SELECT COUNT(*) AS total FROM sqlite_sequence WHERE name=${escapeSqlValue(
tableName
)};`
);

let hasAutoIncrement = false;
const seqRow = seqCount.rows[0];
if (seqRow && Number(seqRow[0] ?? 0) > 0) {
hasAutoIncrement = true;
}

try {
const seqCount = await this.query(
`SELECT COUNT(*) AS total FROM sqlite_sequence WHERE name=${escapeSqlValue(
tableName
)};`
);

const seqRow = seqCount.rows[0];
if (seqRow && Number(seqRow[0] ?? 0) > 0) {
hasAutoIncrement = true;
}
} catch {}

return {
columns,
Expand Down
16 changes: 15 additions & 1 deletion src/lib/validation.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { validateConnectionEndpoint, validateOperation } from "./validation";
import {
normalizeConnectionEndpoint,
validateConnectionEndpoint,
validateOperation,
} from "./validation";

describe("Operation Validation", () => {
it("UPDATE with primary key SHOULD be valid operation", () => {
Expand Down Expand Up @@ -209,4 +213,14 @@ describe("Validate the connection endpoint", () => {
false
);
});

it("Transform libsql:// to wss://", () => {
expect(normalizeConnectionEndpoint("libsql://testing.example.com")).toBe(
"wss://testing.example.com"
);

expect(normalizeConnectionEndpoint("wss://testing.example.com")).toBe(
"wss://testing.example.com"
);
});
});
8 changes: 6 additions & 2 deletions src/lib/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,16 @@ export function validateConnectionEndpoint(
try {
const url = new URL(endpoint);

if (url.protocol !== "wss:") {
return [false, "We only support wss:// at the moment."];
if (url.protocol !== "wss:" && url.protocol !== "libsql:") {
return [false, "We only support wss:// or libsql:// at the moment."];
}

return [true, ""];
} catch {
return [false, "Your URL is not valid"];
}
}

export function normalizeConnectionEndpoint(endpoint: string) {
return endpoint.replace(/^libsql:\/\//, "wss://");
}
Loading

0 comments on commit d6677d2

Please sign in to comment.