Skip to content

Commit

Permalink
feat: implement query page
Browse files Browse the repository at this point in the history
  • Loading branch information
frectonz committed Jun 13, 2024
1 parent 0643045 commit 6fc0b5b
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 18 deletions.
74 changes: 67 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ async fn main() -> color_eyre::Result<()> {
let args = Args::parse();
let db = TheDB::open(args.database)?;

let api = warp::path("api")
.and(handlers::routes(db))
.with(warp::cors().allow_any_origin());
let cors = warp::cors()
.allow_any_origin()
.allow_methods(vec!["GET", "POST", "DELETE"])
.allow_headers(vec!["Content-Length", "Content-Type"]);
let api = warp::path("api").and(handlers::routes(db)).with(cors);
let homepage = warp::get().and_then(statics::homepage);
let statics = statics::routes();

Expand Down Expand Up @@ -318,16 +320,49 @@ impl TheDB {
.filter_map(|x| x.ok())
.collect::<Vec<_>>();

let resp = responses::Table {
color_eyre::eyre::Ok(responses::Table {
name,
sql,
row_count,
table_size,
index_count,
columns,
rows,
};
color_eyre::eyre::Ok(resp)
})
})
.await?
}

async fn query(&self, query: String) -> color_eyre::Result<responses::Query> {
let conn = self.conn.clone();

tokio::task::spawn_blocking(move || {
let conn = conn.lock().or_else(|e| {
tracing::error!("could not get lock: {e}");
color_eyre::eyre::bail!("could not get lock: {e}")
})?;

let mut stmt = conn.prepare(&query)?;
let columns = stmt
.column_names()
.into_iter()
.map(ToOwned::to_owned)
.collect::<Vec<_>>();

let columns_len = columns.len();
let rows = stmt
.query_map((), |r| {
let mut rows = Vec::with_capacity(columns_len);
for i in 0..columns_len {
let val = helpers::value_to_json(r.get_ref(i)?);
rows.push(val);
}
Ok(rows)
})?
.filter_map(|x| x.ok())
.collect::<Vec<_>>();

color_eyre::eyre::Ok(responses::Query { columns, rows })
})
.await?
}
Expand Down Expand Up @@ -399,9 +434,16 @@ mod responses {
pub columns: Vec<String>,
pub rows: Vec<Vec<serde_json::Value>>,
}

#[derive(Serialize)]
pub struct Query {
pub columns: Vec<String>,
pub rows: Vec<Vec<serde_json::Value>>,
}
}

mod handlers {
use serde::Deserialize;
use warp::Filter;

use crate::{rejections, TheDB};
Expand All @@ -428,8 +470,18 @@ mod handlers {
.and(with_state(&db))
.and(warp::path!("tables" / String))
.and_then(table);
let query = warp::post()
.and(with_state(&db))
.and(warp::path!("query"))
.and(warp::body::json::<QueryBody>())
.and_then(query);

overview.or(tables).or(table)
overview.or(tables).or(table).or(query)
}

#[derive(Deserialize)]
pub struct QueryBody {
pub query: String,
}

async fn overview(db: TheDB) -> Result<impl warp::Reply, warp::Rejection> {
Expand All @@ -455,6 +507,14 @@ mod handlers {
})?;
Ok(warp::reply::json(&tables))
}

async fn query(db: TheDB, query: QueryBody) -> Result<impl warp::Reply, warp::Rejection> {
let tables = db
.query(query.query)
.await
.map_err(|_| warp::reject::custom(rejections::InternalServerError))?;
Ok(warp::reply::json(&tables))
}
}

mod rejections {
Expand Down
7 changes: 7 additions & 0 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"monaco-editor": "^0.49.0",
"postcss": "^8.4.38",
"prettier": "^3.3.1",
"tailwindcss": "^3.4.4",
Expand Down
35 changes: 25 additions & 10 deletions ui/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ const overview = z.object({
indexes: z.number(),
triggers: z.number(),
views: z.number(),
counts: z.array(
z.object({
counts: z
.object({
name: z.string(),
count: z.number(),
}),
),
})
.array(),
});

const tables = z.object({
tables: z.array(
z.object({
tables: z
.object({
name: z.string(),
count: z.number(),
}),
),
})
.array(),
});

const table = z.object({
Expand All @@ -43,8 +43,13 @@ const table = z.object({
row_count: z.number(),
index_count: z.number(),
table_size: z.string(),
columns: z.array(z.string()),
rows: z.array(z.array(z.any())),
columns: z.string().array(),
rows: z.any().array().array(),
});

const query = z.object({
columns: z.string().array(),
rows: z.any().array().array(),
});

const $fetch = createZodFetcher();
Expand All @@ -53,3 +58,13 @@ export const fetchOverview = () => $fetch(overview, `${BASE_URL}/`);
export const fetchTables = () => $fetch(tables, `${BASE_URL}/tables`);
export const fetchTable = (name: string) =>
$fetch(table, `${BASE_URL}/tables/${name}`);
export const fetchQuery = (value: string) =>
$fetch(query, `${BASE_URL}/query`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ query: value }),
credentials: "omit",
});
53 changes: 53 additions & 0 deletions ui/src/components/editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import "@/editorWorker";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
import { FunctionComponent, useRef, useState, useEffect } from "react";

import { Card } from "./ui/card";

type Props = {
value: string;
onChange?: (value: string) => void;
};

export const Editor: FunctionComponent<Props> = ({ value, onChange }) => {
const [editor, setEditor] =
useState<monaco.editor.IStandaloneCodeEditor | null>(null);
const monacoEl = useRef(null);

useEffect(() => {
if (monacoEl) {
setEditor((editor) => {
if (editor) return editor;

const newEditor = monaco.editor.create(monacoEl.current!, {
value,
language: "sql",
minimap: {
enabled: false,
},
fontSize: 20,
padding: {
top: 20,
bottom: 20,
},
automaticLayout: true,
readOnly: onChange === undefined,
});

newEditor.onDidChangeModelContent((_) => {
onChange?.(newEditor.getValue());
});

return newEditor;
});
}

return () => editor?.dispose();
}, [monacoEl.current]);

return (
<Card className="p-1">
<div className="w-full h-[200px]" ref={monacoEl}></div>
</Card>
);
};
11 changes: 11 additions & 0 deletions ui/src/editorWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as monaco from "monaco-editor";
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";

// @ts-ignore
self.MonacoEnvironment = {
getWorker() {
return new editorWorker();
},
};

monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
38 changes: 37 additions & 1 deletion ui/src/routes/query.lazy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
import "react-data-grid/lib/styles.css";

import { useState } from "react";
import DataGrid from "react-data-grid";
import { useQuery } from "@tanstack/react-query";
import { createLazyRoute } from "@tanstack/react-router";

import { fetchQuery } from "@/api";
import { Editor } from "@/components/editor";

export const Route = createLazyRoute("/query")({
component: () => <h1 className="text-4xl">Query</h1>,
component: Query,
});

function Query() {
const [code, setCode] = useState("SELECT 1 + 1");

const { data } = useQuery({
queryKey: ["query", code],
queryFn: () => fetchQuery(code),
});

const columns = data?.columns.map((col) => ({ key: col, name: col })) ?? [];
const rows =
data?.rows.map((row) =>
row.reduce((acc, curr, i) => {
acc[data.columns[i]] = curr;
return acc;
}, {}),
) ?? [];

return (
<>
<div className="grid gap-2 grid-cols-1">
<Editor value={code} onChange={(v) => setCode(v)} />
</div>

<DataGrid columns={columns} rows={rows} className="rdg-light" />
</>
);
}
2 changes: 2 additions & 0 deletions ui/src/routes/tables.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,11 @@ function Table({ name }: Props) {
</CardContent>
</Card>
</div>

<Card className="font-mono text-sm">
<CodeBlock text={data.sql} language="sql" showLineNumbers={false} />
</Card>

<DataGrid columns={columns} rows={rows} className="rdg-light" />
</div>
);
Expand Down

0 comments on commit 6fc0b5b

Please sign in to comment.