Skip to content

Commit

Permalink
feat: doc openapi
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin committed Aug 20, 2024
1 parent aa7eaaf commit 9840664
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 35 deletions.
41 changes: 24 additions & 17 deletions apps/backend/src/modules/openapi/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import { CommandBus, QueryBus } from "@undb/cqrs"
import { inject, singleton } from "@undb/di"
import { type ICommandBus, type IQueryBus, PaginatedDTO, Some } from "@undb/domain"
import { createLogger } from "@undb/logger"
import { API_TOKEN_HEADER_NAME, createOpenApiSpec, type IApiTokenService, injectApiTokenService } from "@undb/openapi"
import {
API_TOKEN_HEADER_NAME,
createOpenApiSpec,
type IApiTokenService,
injectApiTokenService,
ViewsOpenApi,
} from "@undb/openapi"
import { injectQueryBuilder, type IQueryBuilder } from "@undb/persistence"
import { GetReadableRecordByIdQuery, GetReadableRecordsQuery } from "@undb/queries"
import { injectSpaceService, type ISpaceService } from "@undb/space"
Expand Down Expand Up @@ -60,7 +66,7 @@ export class OpenAPI {
private spaceService: ISpaceService,
) {}

async getSpec(baseName: string, tableName: string, viewName?: string) {
async getSpec(baseName: string, tableName: string) {
const ts = withUniqueTable({ baseName, tableName }).unwrap()
const table = (await this.repo.findOne(Some(ts))).expect("Table not found")
const base = (await this.baseRepo.findOneById(table.baseId)).expect("Base not found")
Expand All @@ -72,18 +78,21 @@ export class OpenAPI {
})
).values.at(0)

const view = viewName ? table.views.getViewByName(viewName) : undefined
const viewRecord = view
? (
await this.recordsQueryService.getReadableRecords({
tableId: table.id.value,
pagination: { limit: 1 },
viewId: view.id.value,
})
).values.at(0)
: undefined
const viewsOpenApi: ViewsOpenApi[] = []
const views = table.views.views

for (const view of views) {
const viewRecord = (
await this.recordsQueryService.getReadableRecords({
tableId: table.id.value,
pagination: { limit: 1 },
viewId: view.id.value,
})
).values.at(0)
viewsOpenApi.push({ view, record: viewRecord })
}

const spec = createOpenApiSpec(base, table, view, record, viewRecord)
const spec = createOpenApiSpec(base, table, record, viewsOpenApi)
return spec
}

Expand All @@ -105,7 +114,7 @@ export class OpenAPI {
.get(
"/",
async (ctx) => {
const spec = await this.getSpec(ctx.params.baseName, ctx.params.tableName, ctx.query.view)
const spec = await this.getSpec(ctx.params.baseName, ctx.params.tableName)

return `<html>
<head>
Expand Down Expand Up @@ -133,7 +142,6 @@ export class OpenAPI {
},
{
params: t.Object({ baseName: t.String(), tableName: t.String() }),
query: t.Object({ view: t.Optional(t.String()) }),
detail: {
tags: ["Doc"],
summary: "Get OpenAPI documentation for a table",
Expand Down Expand Up @@ -175,12 +183,11 @@ export class OpenAPI {
.get(
"/openapi.json",
async (ctx) => {
const spec = await this.getSpec(ctx.params.baseName, ctx.params.tableName, ctx.query.view)
const spec = await this.getSpec(ctx.params.baseName, ctx.params.tableName)
return spec
},
{
params: t.Object({ baseName: t.String(), tableName: t.String() }),
query: t.Object({ view: t.Optional(t.String()) }),
detail: {
tags: ["Doc"],
summary: "Get OpenAPI documentation json spec for a table",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
<script lang="ts">
import { page } from "$app/stores"
import { getBaseById, bases } from "$lib/store/base.store"
import { getBaseById } from "$lib/store/base.store"
import { getTable } from "$lib/store/table.store"
const table = getTable()
$: base = $getBaseById($table.baseId)
$: viewId = $page.params.viewId
$: view = $table.views.getViewById(viewId)
</script>

{#if $table && base}
<iframe
class="h-full w-full"
src={`/api/bases/${base?.name}/tables/${$table.name.value}?view=${view?.name.value}`}
src={`/api/bases/${base?.name}/tables/${$table.name.value}`}
title={$table.name.value}
frameborder="0"
>
Expand Down
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions packages/openapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@undb/di": "workspace:*",
"@undb/domain": "workspace:*",
"@undb/space": "workspace:*",
"@undb/base": "workspace:*",
"@undb/table": "workspace:*",
"@undb/zod": "workspace:*",
"radash": "^12.1.0"
Expand Down
18 changes: 11 additions & 7 deletions packages/openapi/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ import {

export const API_TOKEN_HEADER_NAME = "x-undb-api-token"

export interface ViewsOpenApi {
view: View
record?: IReadableRecordDTO
}

export const createOpenApiSpec = (
base: Base,
table: TableDo,
view?: View,
record?: IReadableRecordDTO,
viewRecord?: IReadableRecordDTO,
record: IReadableRecordDTO | undefined,
views: ViewsOpenApi[],
) => {
const registry = new OpenAPIRegistry()

Expand All @@ -57,11 +61,11 @@ export const createOpenApiSpec = (
recordSubscription(base, table),
]

if (view) {
const viewRecordSchema = createRecordComponent(table, view, viewRecord)
for (const { view, record } of views) {
const viewRecordSchema = createRecordComponent(table, view, record)
registry.register(
VIEW_RECORD_COMPONENT,
viewRecordSchema.openapi({ description: table.name.value + " view record schema" }),
view.name.value + ":" + VIEW_RECORD_COMPONENT,
viewRecordSchema.openapi({ description: table.name.value + " " + view.name.value + " view record schema" }),
)

routes.push(getViewRecords(base, table, view, viewRecordSchema))
Expand Down
20 changes: 14 additions & 6 deletions packages/openapi/src/openapi/record.openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import { z, type ZodTypeAny } from "@undb/zod"

export const RECORD_ID_COMPONENT = "RecordId"
export const RECORD_COMPONENT = "Record"
export const VIEW_COMPONENT = "View"
export const VIEW_RECORD_COMPONENT = "ViewRecord"
export const RECORD_VALUES_COMPONENT = "RecordValues"
export const VIEW_RECORD_VALUES_COMPONENT = "ViewRecordValues"
export const RECORD_DISPLAY_VALUES_COMPONENT = "RecordDisplayValues"
export const VIEW_RECORD_DISPLAY_VALUES_COMPONENT = "ViewRecordDisplayValues"

export const createRecordComponent = (table: TableDo, view?: View, record?: IReadableRecordDTO) => {
const schema = view ? table.schema.getViewReadableSchema(table, view) : table.schema.readableSchema
Expand All @@ -19,8 +22,13 @@ export const createRecordComponent = (table: TableDo, view?: View, record?: IRea
return z
.object({
id: recordId.openapi(RECORD_ID_COMPONENT, { example: record?.id }),
values: schema.openapi(RECORD_VALUES_COMPONENT, { example }),
displayValues: displayScheam.openapi(RECORD_DISPLAY_VALUES_COMPONENT, { example: displayExample }),
values: schema.openapi(view ? view.name.value + ":" + VIEW_RECORD_VALUES_COMPONENT : RECORD_VALUES_COMPONENT, {
example,
}),
displayValues: displayScheam.openapi(
view ? view.name.value + ":" + VIEW_RECORD_DISPLAY_VALUES_COMPONENT : RECORD_DISPLAY_VALUES_COMPONENT,
{ example: displayExample },
),
})
.openapi(view ? VIEW_RECORD_COMPONENT : RECORD_COMPONENT, {
description: view ? `record in ${view.name.value} view` : "record",
Expand Down Expand Up @@ -56,15 +64,15 @@ export const getViewRecords = (base: Base, table: TableDo, view: View, recordSch
path: `/bases/${base.name.value}/tables/${table.name.value}/views/${view.name.value}/records`,
description: `Get ${table.name.value} records in ${view.name.value} view`,
summary: `Get ${table.name.value} records in ${view.name.value} view`,
tags: [RECORD_COMPONENT],
tags: [RECORD_COMPONENT, VIEW_COMPONENT],
responses: {
200: {
description: "record data",
content: {
"application/json": {
schema: z.object({
total: z.number().int().positive(),
records: z.array(recordSchema.openapi(VIEW_RECORD_COMPONENT)),
records: z.array(recordSchema.openapi(view.name.value + ":" + VIEW_RECORD_COMPONENT)),
}),
},
},
Expand Down Expand Up @@ -106,7 +114,7 @@ export const getViewRecordById = (base: Base, table: TableDo, view: View, record
path: `/bases/${base.name.value}/tables/${table.name.value}/views/${view.name.value}/records/{recordId}`,
description: `Get ${table.name.value} record by id in ${view.name.value} view`,
summary: `Get ${table.name.value} record by id in ${view.name.value} view`,
tags: [RECORD_COMPONENT],
tags: [RECORD_COMPONENT, VIEW_COMPONENT],
request: {
params: z.object({
recordId: recordId,
Expand All @@ -118,7 +126,7 @@ export const getViewRecordById = (base: Base, table: TableDo, view: View, record
content: {
"application/json": {
schema: z.object({
data: recordSchema.openapi(VIEW_RECORD_COMPONENT).nullable(),
data: recordSchema.openapi(view.name.value + ":" + VIEW_RECORD_COMPONENT).nullable(),
}),
},
},
Expand Down

0 comments on commit 9840664

Please sign in to comment.