Skip to content

Commit

Permalink
feat: simple database er view
Browse files Browse the repository at this point in the history
  • Loading branch information
morlay committed Dec 30, 2024
1 parent 2d1c57c commit 57be2f6
Show file tree
Hide file tree
Showing 2 changed files with 268 additions and 8 deletions.
35 changes: 27 additions & 8 deletions webapp/openapi-playground/mod/openapi/OperationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Box, styled } from "@innoai-tech/vueuikit";
import { ResponseView } from "./ResponseView.tsx";
import { RequestBuilder } from "./RequestBuilder.tsx";
import { Markdown } from "@innoai-tech/vuemarkdown";
import { DatabaseErContainer } from "./components/DatabaseErView.tsx";

export const OperationView = component$({
operationId: t.string()
Expand Down Expand Up @@ -87,24 +88,32 @@ export const OperationView = component$({
if (!op) {
return null;
}
if (op.operationId == "SycDatabaseEr") {
return (
<OperationContainer key={op.operationId}>
{$heading}
{$desc}
<OperationMain>
<DatabaseErContainer
op={op}
/>
</OperationMain>
</OperationContainer>
)
}

return (
<OperationContainer key={op.operationId}>
{$heading}
{$desc}
<Box sx={{
flex: 1,
overflow: "hidden",
display: "flex",
flexDirection: "column",
alignItems: "stretch"
}}>
<OperationMain>
<Box sx={{
flex: 1,
overflow: "auto"
}}>
{$requestBuilder}
</Box>
</Box>
</OperationMain>
</OperationContainer>
);
})
Expand All @@ -118,6 +127,16 @@ const OperationContainer = styled("div")({
alignItems: "stretch"
});


const OperationMain = styled("div")({
flex: 1,
overflow: "hidden",
display: "flex",
flexDirection: "column",
alignItems: "stretch"
});


const OperationHeading = styled("div")({
display: "flex",
alignItems: "center",
Expand Down
241 changes: 241 additions & 0 deletions webapp/openapi-playground/mod/openapi/components/DatabaseErView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import { component, component$, rx } from "@innoai-tech/vuekit";
import type { OperationWithMethodPath } from "../models";
import { OpenAPIProvider } from "../OpenAPIProvider.tsx";
import { onMounted } from "vue";
import { alpha, styled, variant } from "@innoai-tech/vueuikit";
import { Icon, mdiKey } from "@innoai-tech/vuematerial";
import { mdiKeyOutline } from "@mdi/js";

export type ErDatabase = {
name: string
tables: Record<string, ErTable>
}

export type ErTable = {
title?: string
description?: string
columns: Record<string, ErColumn>
constraints: Record<string, ErConstraint>
}

export type ErColumn = {
type: string
title?: string
description?: string
of?: string
}

export type ErConstraint = {
title?: string
description?: string
columnNames: Array<string>
method?: string
unique?: boolean
primary?: boolean
}


export const DatabaseErContainer = component$<{
op: OperationWithMethodPath
}>((props, { render }) => {

const openapi$ = OpenAPIProvider.use();

onMounted(() => {
openapi$.request(props.op.operationId, {});
});

return rx(
openapi$.response$(props.op.operationId),
render((resp) => {
return <DatabaseErView database={resp.body} />;
})
);
});

export const DatabaseErView = component<{
database: ErDatabase
}>((props) => {
return () => {
return (
<DatabaseErMain>
<DatabaseErHeader>
{props.database.name}
</DatabaseErHeader>
{Object.keys(props.database.tables).toSorted().map((tableName) => {
const t = props.database.tables[tableName] ?? { columns: {}, constraints: {} };

const columns = t.columns;
const constraints = t.constraints;

return (
<DatabaseErTable open={true}>
<DatabaseErTableSummary>
<span>
{tableName}
</span>
<Spacer />
<span>
{t.title}
</span>
</DatabaseErTableSummary>
{Object.keys(columns).toSorted().map((colName) => {
const col = columns[colName]!;

return (
<DatabaseErTableDef>
<DatabaseErTableColumnName>
{colName}
</DatabaseErTableColumnName>
{col.of ? (
<DatabaseErTableColumnOf>
{col.of}
</DatabaseErTableColumnOf>
) : (
<DatabaseErTableColumnType>
{col.type}
</DatabaseErTableColumnType>
)}
<DatabaseErTableColumnComment>
{col.title}
</DatabaseErTableColumnComment>
</DatabaseErTableDef>
);
})}

{Object.entries(constraints).toSorted(([name1, c1], [name2, c2]) => {
if (c2.primary && !c1.primary) {
return 1;
}

if (!c2.primary && c1.primary) {
return -1;
}

if (c2.unique && !c1.unique) {
return 1;
}

if (!c2.unique && c1.unique) {
return -1;
}

return name1.localeCompare(name2);
}).map(([constraintName, constraint]) => {

return (
<DatabaseErTableDef>
<DatabaseErTableConstraintName
data-primary={constraint.primary}
>
<Icon path={constraint.unique ? mdiKeyOutline : mdiKey} />
<span>
{constraintName}
</span>
<span>
({constraint.columnNames.join(",")})
</span>
</DatabaseErTableConstraintName>
</DatabaseErTableDef>
);
})}
</DatabaseErTable>
);
})}
</DatabaseErMain>
);
};
});


const DatabaseErMain = styled("div")({
py: 18,
px: 24,
flex: 1,
overflow: "auto"
});

const DatabaseErHeader = styled("div")({
py: 8,
px: 16,
textStyle: "sys.label-large"
});


const DatabaseErTable = styled("details")({
"& + &": {
mt: 16
},

rounded: "sm",
border: "1px solid",
borderColor: variant("sys.outline-variant", alpha(0.38)),
overflow: "hidden"
});

const DatabaseErTableSummary = styled("summary")({
textStyle: "sys.label-large",
py: 8,
px: 16,
display: "flex",
color: "sys.primary"
});


const DatabaseErTableDef = styled("div")({
px: 16,
py: 4,
display: "flex",
"&:hover": {
containerStyle: "sys.surface-container-low"
}
});


export const DatabaseErTableColumnName = styled("div")({
display: "flex",
width: "20vw",
textStyle: "sys.label-small",
font: "code"
});


export const DatabaseErTableColumnComment = styled("div")({
display: "flex",
textStyle: "sys.label-small",
width: "20vw",
justifyContent: "end"
});

export const DatabaseErTableColumnOf = styled("div")({
flex: 1,
textStyle: "sys.label-small",
font: "code",
color: "sys.primary"
});


export const DatabaseErTableColumnType = styled("div")({
flex: 1,
textStyle: "sys.label-small"
});


export const DatabaseErTableConstraintName = styled("div")({
flex: 1,
textStyle: "sys.label-small",
font: "code",
display: "flex",
alignItems: "center",
gap: "1em",

_primary: {
[`${Icon}`]: {
color: "sys.primary"
}
}
});

const Spacer = styled("div")({
flex: 1
});

0 comments on commit 57be2f6

Please sign in to comment.