diff --git a/client/testfixture/config.textproto b/client/testfixture/config.textproto
index 2652887..fd9091b 100644
--- a/client/testfixture/config.textproto
+++ b/client/testfixture/config.textproto
@@ -43,14 +43,6 @@ record_apis: [
schemas: [
{
name: "simple_schema"
- schema:
- "{"
- " \"type\": \"object\","
- " \"properties\": {"
- " \"name\": { \"type\": \"string\" },"
- " \"obj\": { \"type\": \"object\" }"
- " },"
- " \"required\": [\"name\"]"
- "}"
+ schema: "{ \"type\": \"object\", \"properties\": { \"name\": { \"type\": \"string\" }, \"obj\": { \"type\": \"object\" } }, \"required\": [\"name\"]}"
}
]
diff --git a/trailbase-core/js/admin/src/components/FilterBar.tsx b/trailbase-core/js/admin/src/components/FilterBar.tsx
index 8298e01..0ba9c4e 100644
--- a/trailbase-core/js/admin/src/components/FilterBar.tsx
+++ b/trailbase-core/js/admin/src/components/FilterBar.tsx
@@ -1,5 +1,3 @@
-import { createSignal } from "solid-js";
-
import { Button } from "@/components/ui/button";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
@@ -9,12 +7,13 @@ export function FilterBar(props: {
initial?: string;
onSubmit: (filter: string) => void;
}) {
- const [input, setInput] = createSignal(props.initial ?? "");
-
+ let ref: HTMLInputElement | undefined;
const onSubmit = () => {
- const value = input();
+ const value = ref?.value;
console.debug("set filter: ", value);
- props.onSubmit(value);
+ if (value !== undefined) {
+ props.onSubmit(value);
+ }
};
return (
@@ -26,12 +25,10 @@ export function FilterBar(props: {
>
{
- const value = (e.currentTarget as HTMLInputElement).value;
- setInput(value);
- }}
/>
diff --git a/trailbase-core/js/admin/src/components/tables/CreateAlterTable.tsx b/trailbase-core/js/admin/src/components/tables/CreateAlterTable.tsx
index 2fac6a4..e46f0e2 100644
--- a/trailbase-core/js/admin/src/components/tables/CreateAlterTable.tsx
+++ b/trailbase-core/js/admin/src/components/tables/CreateAlterTable.tsx
@@ -66,6 +66,7 @@ import {
} from "@/components/FormFields";
import type { FormType, AnyFieldApi } from "@/components/FormFields";
import { SheetContainer } from "@/components/SafeSheet";
+import { invalidateConfig } from "@/lib/config";
export function CreateAlterTableForm(props: {
close: () => void;
@@ -108,6 +109,7 @@ export function CreateAlterTableForm(props: {
}
if (!dryRun) {
+ invalidateConfig();
props.schemaRefetch().then(() => {
props.setSelected(value.name);
});
diff --git a/trailbase-core/js/admin/src/components/tables/TablesPage.tsx b/trailbase-core/js/admin/src/components/tables/TablesPage.tsx
index 184cb03..b0a1642 100644
--- a/trailbase-core/js/admin/src/components/tables/TablesPage.tsx
+++ b/trailbase-core/js/admin/src/components/tables/TablesPage.tsx
@@ -70,7 +70,7 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
-import { createConfigQuery } from "@/lib/config";
+import { createConfigQuery, invalidateConfig } from "@/lib/config";
import { adminFetch } from "@/lib/fetch";
import { urlSafeBase64ToUuid, showSaveFileDialog } from "@/lib/utils";
import { RecordApiConfig } from "@proto/config";
@@ -339,6 +339,8 @@ function TableHeaderRightHandButtons(props: {
await dropTable({
name: table().name,
});
+
+ invalidateConfig();
props.schemaRefetch();
}}
msg="Deleting a table will irreversibly delete all the data contained. Are you sure you'd like to continue?"
@@ -518,13 +520,13 @@ type TableStore = {
schemas: ListSchemasResponse;
// Filter & pagination
- filter: string | undefined;
+ filter: string | null;
pagination: PaginationState;
};
type FetchArgs = {
tableName: string;
- filter: string | undefined;
+ filter: string | null;
pageSize: number;
pageIndex: number;
cursors: string[];
@@ -700,7 +702,7 @@ function RowDataTable(props: {
{
if (value === props.state.store.filter) {
refetch();
@@ -831,11 +833,11 @@ function TablePane(props: {
pageSize?: string;
}>();
- function newStore(): TableStore {
+ function newStore({ filter }: { filter: string | null }): TableStore {
return {
selected: props.selectedTable,
schemas: props.schemas,
- filter: searchParams.filter ?? "",
+ filter,
pagination: defaultPaginationState({
// NOTE: We index has to start at 0 since we're building the list of
// stable cursors as we incrementally page.
@@ -847,14 +849,25 @@ function TablePane(props: {
// Cursors are deliberately kept out of the store to avoid tracking.
let cursors: string[] = [];
- const [store, setStore] = createStore(newStore());
+ const [store, setStore] = createStore(
+ newStore({ filter: searchParams.filter ?? null }),
+ );
createEffect(() => {
+ // When switching tables/views, recreate the state. This includes the main
+ // store but also the current search params and the untracked cursors.
if (store.selected.name !== props.selectedTable.name) {
- // Recreate the state/store when we switch tables.
cursors = [];
- setStore(newStore());
+
+ // The new table probably has different schema, thus filters must not
+ // carry over.
+ const newFilter = { filter: null };
+ setSearchParams(newFilter);
+
+ setStore(newStore(newFilter));
+ return;
}
+ // When the filter changes, we also update the search params to be in sync.
setSearchParams({
filter: store.filter,
});
diff --git a/trailbase-core/js/admin/src/lib/config.ts b/trailbase-core/js/admin/src/lib/config.ts
index f7c4b33..991c783 100644
--- a/trailbase-core/js/admin/src/lib/config.ts
+++ b/trailbase-core/js/admin/src/lib/config.ts
@@ -11,7 +11,7 @@ function createClient(): QueryClient {
}
const queryClient = createClient();
-export async function setConfig(config: Config) {
+export async function setConfig(config: Config): Promise {
const data = queryClient.getQueryData(defaultKey);
const hash = data?.hash;
if (!hash) {
@@ -24,11 +24,13 @@ export async function setConfig(config: Config) {
hash,
};
console.debug("Updating config:", request);
- const response = await updateConfig(request);
+ await updateConfig(request);
- queryClient.invalidateQueries();
+ invalidateConfig();
+}
- return response;
+export function invalidateConfig() {
+ queryClient.invalidateQueries();
}
export function createConfigQuery() {
diff --git a/trailbase-core/js/admin/src/lib/schema.ts b/trailbase-core/js/admin/src/lib/schema.ts
index 9f6d4f4..5d6819f 100644
--- a/trailbase-core/js/admin/src/lib/schema.ts
+++ b/trailbase-core/js/admin/src/lib/schema.ts
@@ -256,5 +256,5 @@ export function tableType(table: Table | View): TableType {
}
export function hiddenTable(table: Table | View): boolean {
- return table.name.startsWith("_");
+ return table.name.startsWith("_") || table.name.startsWith("sqlite_");
}
diff --git a/trailbase-core/src/admin/list_logs.rs b/trailbase-core/src/admin/list_logs.rs
index 61daa9e..4209bb5 100644
--- a/trailbase-core/src/admin/list_logs.rs
+++ b/trailbase-core/src/admin/list_logs.rs
@@ -108,7 +108,8 @@ pub async fn list_logs_handler(
limit,
order,
..
- } = parse_query(raw_url_query.as_deref()).unwrap_or_default();
+ } = parse_query(raw_url_query.as_deref())
+ .map_err(|err| Error::Precondition(format!("Invalid query '{err}': {raw_url_query:?}")))?;
// NOTE: We cannot use state.table_metadata() here, since we're working on the logs database.
// We could cache, however this is just the admin logs handler.
diff --git a/trailbase-core/src/admin/rows/list_rows.rs b/trailbase-core/src/admin/rows/list_rows.rs
index de2c04c..0accabe 100644
--- a/trailbase-core/src/admin/rows/list_rows.rs
+++ b/trailbase-core/src/admin/rows/list_rows.rs
@@ -32,8 +32,6 @@ pub async fn list_rows_handler(
Path(table_name): Path,
RawQuery(raw_url_query): RawQuery,
) -> Result, Error> {
- // TODO: we should probably return an error if the query parsing fails rather than quietly
- // falling back to defaults.
let QueryParseResult {
params: filter_params,
cursor,
@@ -41,7 +39,8 @@ pub async fn list_rows_handler(
order,
offset,
..
- } = parse_query(raw_url_query.as_deref()).unwrap_or_default();
+ } = parse_query(raw_url_query.as_deref())
+ .map_err(|err| Error::Precondition(format!("Invalid query '{err}': {raw_url_query:?}")))?;
let (virtual_table, table_or_view_metadata): (bool, Arc) = {
if let Some(table_metadata) = state.table_metadata().get(&table_name) {
diff --git a/trailbase-core/src/admin/user/list_users.rs b/trailbase-core/src/admin/user/list_users.rs
index 81e5338..2d04779 100644
--- a/trailbase-core/src/admin/user/list_users.rs
+++ b/trailbase-core/src/admin/user/list_users.rs
@@ -69,7 +69,8 @@ pub async fn list_users_handler(
limit,
order,
..
- } = parse_query(raw_url_query.as_deref()).unwrap_or_default();
+ } = parse_query(raw_url_query.as_deref())
+ .map_err(|err| Error::Precondition(format!("Invalid query '{err}': {raw_url_query:?}")))?;
let Some(table_metadata) = state.table_metadata().get(USER_TABLE) else {
return Err(Error::Precondition(format!("Table {USER_TABLE} not found")));
diff --git a/trailbase-core/src/records/list_records.rs b/trailbase-core/src/records/list_records.rs
index 847b019..33e295b 100644
--- a/trailbase-core/src/records/list_records.rs
+++ b/trailbase-core/src/records/list_records.rs
@@ -55,20 +55,16 @@ pub async fn list_records_handler(
return Err(RecordError::Internal("missing pk column".into()));
};
- let Ok(QueryParseResult {
+ let QueryParseResult {
params: filter_params,
cursor,
limit,
order,
count,
..
- }) = parse_query(raw_url_query.as_deref())
- else {
- #[cfg(test)]
- log::error!("{:?}", raw_url_query);
-
- return Err(RecordError::BadRequest("Bad query"));
- };
+ } = parse_query(raw_url_query.as_deref()).map_err(|_err| {
+ return RecordError::BadRequest("Invalid query");
+ })?;
// Where clause contains column filters and cursor depending on what's present.
let WhereClause {