feat(api): complete refactor of openapi routes
Some checks failed
Build and Push Docker Image / tests (push) Failing after 1m4s
Build and Push Docker Image / build (push) Has been skipped

This commit is contained in:
Lars Hampe 2024-11-06 22:20:10 +01:00
parent 95e00816c4
commit f161d6b468
7 changed files with 149 additions and 260 deletions

View File

@ -4,6 +4,10 @@ import { createRoute } from '@hono/zod-openapi'
import { and, eq } from 'drizzle-orm'
import { HTTPException } from 'hono/http-exception'
import type { changelogApi } from '.'
import { verifyAuthentication } from '../utils/authentication'
import { openApiErrorResponses, openApiSecurity } from '../utils/openapi'
export const route = createRoute({
method: 'get',
path: '/:id',
@ -20,39 +24,35 @@ export const route = createRoute({
},
description: 'Return changelog by id',
},
400: {
description: 'Bad Request',
},
500: {
description: 'Internal Server Error',
},
...openApiErrorResponses,
},
...openApiSecurity,
})
export const func = async ({ userId, id }: { userId: string; id: string }) => {
const result = await db.query.changelog.findFirst({
where: and(eq(changelog.userId, userId), eq(changelog.id, id)),
with: {
pages: {
with: {
page: true,
export const registerChangelogById = (api: typeof changelogApi) => {
return api.openapi(route, async (c) => {
const userId = await verifyAuthentication(c)
const { id } = c.req.valid('param')
const result = await db.query.changelog.findFirst({
where: and(eq(changelog.userId, userId), eq(changelog.id, id)),
with: {
pages: {
with: {
page: true,
},
},
versions: {
orderBy: (changelog_version, { desc }) => [
desc(changelog_version.createdAt),
],
},
},
versions: {
orderBy: (changelog_version, { desc }) => [
desc(changelog_version.createdAt),
],
},
},
})
if (!result) {
throw new HTTPException(404, { message: 'Not found' })
}
return c.json(ChangelogOutput.parse(result), 200)
})
if (!result) {
throw new HTTPException(404, { message: 'Not found' })
}
return result
}
export default {
route,
func,
}

View File

@ -5,6 +5,10 @@ import {
} from '@boring.tools/schema'
import { createRoute, type z } from '@hono/zod-openapi'
import type { changelogApi } from '.'
import { verifyAuthentication } from '../utils/authentication'
import { openApiErrorResponses, openApiSecurity } from '../utils/openapi'
export const route = createRoute({
method: 'post',
path: '/',
@ -23,32 +27,24 @@ export const route = createRoute({
},
description: 'Return created changelog',
},
400: {
description: 'Bad Request',
},
500: {
description: 'Internal Server Error',
},
...openApiErrorResponses,
},
...openApiSecurity,
})
export const func = async ({
userId,
payload,
}: {
userId: string
payload: z.infer<typeof ChangelogCreateInput>
}) => {
return await db
.insert(changelog)
.values({
...payload,
userId: userId,
})
.returning()
}
export const registerChangelogCreate = (api: typeof changelogApi) => {
return api.openapi(route, async (c) => {
const userId = await verifyAuthentication(c)
const payload: z.infer<typeof ChangelogCreateInput> = await c.req.json()
export default {
route,
func,
const [result] = await db
.insert(changelog)
.values({
...payload,
userId: userId,
})
.returning()
return c.json(ChangelogCreateOutput.parse(result), 201)
})
}

View File

@ -4,6 +4,10 @@ import { createRoute } from '@hono/zod-openapi'
import { and, eq } from 'drizzle-orm'
import { HTTPException } from 'hono/http-exception'
import type { changelogApi } from '.'
import { verifyAuthentication } from '../utils/authentication'
import { openApiErrorResponses, openApiSecurity } from '../utils/openapi'
export const route = createRoute({
method: 'delete',
path: '/:id',
@ -17,29 +21,25 @@ export const route = createRoute({
},
description: 'Removes a changelog by id',
},
400: {
description: 'Bad Request',
},
500: {
description: 'Internal Server Error',
},
...openApiErrorResponses,
},
...openApiSecurity,
})
export const func = async ({ userId, id }: { userId: string; id: string }) => {
const result = await db
.delete(changelog)
.where(and(eq(changelog.userId, userId), eq(changelog.id, id)))
.returning()
export const registerChangelogDelete = async (api: typeof changelogApi) => {
return api.openapi(route, async (c) => {
const userId = await verifyAuthentication(c)
const id = c.req.param('id')
if (!result) {
throw new HTTPException(404, { message: 'Not found' })
}
const [result] = await db
.delete(changelog)
.where(and(eq(changelog.userId, userId), eq(changelog.id, id)))
.returning()
return result
}
export default {
route,
func,
if (!result) {
throw new HTTPException(404, { message: 'Not found' })
}
return c.json(GeneralOutput.parse(result), 200)
})
}

View File

@ -1,135 +1,28 @@
import { OpenAPIHono } from '@hono/zod-openapi'
import { cors } from 'hono/cors'
import type { Variables } from '..'
import { verifyAuthentication } from '../utils/authentication'
import { type ContextModule, captureSentry } from '../utils/sentry'
import ById from './byId'
import type { ContextModule } from '../utils/sentry'
import { registerChangelogById } from './byId'
import { changelogCommitApi } from './commit'
import Create from './create'
import Delete from './delete'
import List from './list'
import Update from './update'
import { registerChangelogCreate } from './create'
import { registerChangelogDelete } from './delete'
import { registerChangelogList } from './list'
import { registerChangelogUpdate } from './update'
import version from './version'
const app = new OpenAPIHono<{ Variables: Variables }>()
export const changelogApi = new OpenAPIHono<{ Variables: Variables }>()
const module: ContextModule = {
name: 'changelog',
}
app.use('*', cors())
app.route('/commit', changelogCommitApi)
app.route('/version', version)
app.openapi(ById.route, async (c) => {
const userId = await verifyAuthentication(c)
try {
const id = c.req.param('id')
const result = await ById.func({ userId, id })
return c.json(result, 200)
} catch (error) {
return captureSentry({
c,
error,
module,
user: {
id: userId,
},
})
}
})
changelogApi.use('*', cors())
changelogApi.route('/commit', changelogCommitApi)
changelogApi.route('/version', version)
app.openapi(List.route, async (c) => {
const userId = await verifyAuthentication(c)
try {
const result = await List.func({ userId })
return c.json(result, 200)
} catch (error) {
return captureSentry({
c,
error,
module,
user: {
id: userId,
},
})
}
})
registerChangelogById(changelogApi)
registerChangelogCreate(changelogApi)
registerChangelogDelete(changelogApi)
registerChangelogUpdate(changelogApi)
registerChangelogList(changelogApi)
app.openapi(Create.route, async (c) => {
const userId = await verifyAuthentication(c)
try {
const [result] = await Create.func({
userId,
payload: await c.req.json(),
})
return c.json(result, 201)
} catch (error) {
return captureSentry({
c,
error,
module,
user: {
id: userId,
},
})
}
})
app.openapi(Delete.route, async (c) => {
const userId = await verifyAuthentication(c)
try {
const id = c.req.param('id')
const result = await Delete.func({ userId, id })
if (result.length === 0) {
return c.json({ message: 'Changelog not found' }, 404)
}
return c.json({ message: 'Changelog removed' })
} catch (error) {
return captureSentry({
c,
error,
module,
user: {
id: userId,
},
})
}
})
app.openapi(Update.route, async (c) => {
const userId = await verifyAuthentication(c)
try {
const id = c.req.param('id')
if (!id) {
return c.json({ message: 'Changelog not found' }, 404)
}
const result = await Update.func({
userId,
payload: await c.req.json(),
id,
})
if (result.length === 0) {
return c.json({ message: 'Changelog not found' }, 404)
}
return c.json(result)
} catch (error) {
return captureSentry({
c,
error,
module,
user: {
id: userId,
},
})
}
})
export default app
export default changelogApi

View File

@ -3,6 +3,10 @@ import { ChangelogListOutput } from '@boring.tools/schema'
import { createRoute } from '@hono/zod-openapi'
import { eq } from 'drizzle-orm'
import type { changelogApi } from '.'
import { verifyAuthentication } from '../utils/authentication'
import { openApiErrorResponses, openApiSecurity } from '../utils/openapi'
export const route = createRoute({
method: 'get',
path: '/',
@ -16,40 +20,37 @@ export const route = createRoute({
},
description: 'Return changelogs for current user',
},
400: {
description: 'Bad Request',
},
500: {
description: 'Internal Server Error',
},
...openApiErrorResponses,
},
...openApiSecurity,
})
export const func = async ({ userId }: { userId: string }) => {
const result = await db.query.changelog.findMany({
where: eq(changelog.userId, userId),
with: {
versions: true,
commits: {
columns: { id: true },
},
},
orderBy: (changelog, { asc }) => [asc(changelog.createdAt)],
})
export const registerChangelogList = (api: typeof changelogApi) => {
return api.openapi(route, async (c) => {
const userId = await verifyAuthentication(c)
return result.map((changelog) => {
const { versions, commits, ...rest } = changelog
return {
...rest,
computed: {
versionCount: versions.length,
commitCount: commits.length,
const result = await db.query.changelog.findMany({
where: eq(changelog.userId, userId),
with: {
versions: true,
commits: {
columns: { id: true },
},
},
}
orderBy: (changelog, { asc }) => [asc(changelog.createdAt)],
})
const mappedData = result.map((changelog) => {
const { versions, commits, ...rest } = changelog
return {
...rest,
computed: {
versionCount: versions.length,
commitCount: commits.length,
},
}
})
return c.json(ChangelogListOutput.parse(mappedData), 200)
})
}
export default {
route,
func,
}

View File

@ -6,6 +6,10 @@ import {
import { createRoute, type z } from '@hono/zod-openapi'
import { and, eq } from 'drizzle-orm'
import { HTTPException } from 'hono/http-exception'
import type { changelogApi } from '.'
import { verifyAuthentication } from '../utils/authentication'
import { openApiErrorResponses, openApiSecurity } from '../utils/openapi'
import { redis } from '../utils/redis'
export const route = createRoute({
@ -26,43 +30,32 @@ export const route = createRoute({
},
description: 'Return updated changelog',
},
400: {
description: 'Bad Request',
},
500: {
description: 'Internal Server Error',
},
...openApiErrorResponses,
},
...openApiSecurity,
})
export const func = async ({
userId,
payload,
id,
}: {
userId: string
payload: z.infer<typeof ChangelogUpdateInput>
id: string
}) => {
const [result] = await db
.update(changelog)
.set({
...payload,
})
.where(and(eq(changelog.id, id), eq(changelog.userId, userId)))
.returning()
export const registerChangelogUpdate = (api: typeof changelogApi) => {
return api.openapi(route, async (c) => {
const userId = await verifyAuthentication(c)
const id = c.req.param('id')
const payload: z.infer<typeof ChangelogUpdateInput> = await c.req.json()
if (!result) {
throw new HTTPException(404, { message: 'Not found' })
}
const [result] = await db
.update(changelog)
.set({
...payload,
})
.where(and(eq(changelog.id, id), eq(changelog.userId, userId)))
.returning()
if (result.pageId) {
redis.del(result.pageId)
}
return result
}
export default {
route,
func,
if (!result) {
throw new HTTPException(404, { message: 'Not found' })
}
if (result.pageId) {
redis.del(result.pageId)
}
return c.json(ChangelogUpdateOutput.parse(result), 200)
})
}

View File

@ -13,6 +13,12 @@ export const ChangelogOutput = z
example: 'This is a changelog',
}),
versions: z.array(VersionOutput).optional(),
isSemver: z.boolean().openapi({
example: true,
}),
isConventional: z.boolean().openapi({
example: true,
}),
computed: z
.object({
versionCount: z.number().openapi({