18. Overtime Controllers
File and Folder structure
src4 collapsed lines
├── configure│ └── openapi│ ├── setup-openapi.ts│ └── setup-scalar-docs.ts├── controllers7 collapsed lines
│ ├── employees│ │ ├── delete.ts│ │ ├── get.ts│ │ ├── index.ts│ │ ├── post.ts│ │ └── put.ts│ ├── healthz.ts│ └── overtimes│ ├── get.ts│ ├── index.ts│ └── post.ts45 collapsed lines
├── global.d.ts├── index.ts├── repositories│ ├── employees│ │ ├── creates.ts│ │ ├── finds.ts│ │ ├── index.ts│ │ ├── removes.ts│ │ └── updates.ts│ ├── overtimes│ │ ├── creates.ts│ │ ├── finds.ts│ │ ├── index.ts│ │ ├── removes.ts│ │ └── updates.ts│ └── prisma.ts├── schema│ ├── branded.ts│ ├── employee.ts│ ├── employeeWithRelations.ts│ ├── general.ts│ ├── helpers.ts│ ├── index.ts│ ├── overtime.ts│ └── overtimeWithRelations.ts├── services│ ├── employee│ │ ├── create.ts│ │ ├── finds.ts│ │ ├── index.ts│ │ ├── removes.ts│ │ └── updates.ts│ └── overtime│ ├── creates.ts│ ├── finds.ts│ ├── index.ts│ ├── removes.ts│ └── updates.ts└── types ├── repositories │ ├── employee.ts │ └── overtime.ts └── services ├── employee.ts └── overtime.ts
POST /
src/controllers/overtimes/post.ts
import type { OvertimeService } from "../../types/services/overtime.js"import { Hono } from "hono"import { describeRoute } from "hono-openapi"import { resolver, validator } from "hono-openapi/effect"import { OvertimeSchema } from "../../schema/index.js"
const createOvertimeResponseSchema = OvertimeSchema.Schema.omit("deletedAt")
const createOvertimeDocs = describeRoute({ responses: { 201: { content: { "application/json": { schema: resolver(createOvertimeResponseSchema), }, }, description: "Get Employee by EmployeeId", }, }, tags: ["Overtime"],})
const createOvertimeValidateRequest = validator("json", OvertimeSchema.CreateSchema)
export function setupPosts(overtimeService: OvertimeService) { const app = new Hono()
app.post("/", createOvertimeDocs, createOvertimeValidateRequest, async (c) => { const body = c.req.valid("json") const overtime = await overtimeService.create(body)
const resObj = createOvertimeResponseSchema.make(overtime) return c.json(resObj, 201) })
return app}
GET /
src/controllers/overtimes/get.ts
import type { EmployeeService } from "../../types/services/employee.js"import type { OvertimeService } from "../../types/services/overtime.js"import * as S from "effect/Schema"import { Hono } from "hono"import { describeRoute } from "hono-openapi"import { resolver, validator } from "hono-openapi/effect"import { Branded, EmployeeSchema, Helpers, OvertimeWithRelationsSchema } from "../../schema/index.js"
const getManyResponseSchema = S.Array(OvertimeWithRelationsSchema.Schema.omit("deletedAt"))
const getManyDocs = describeRoute({ responses: { 200: { content: { "application/json": { schema: resolver(getManyResponseSchema), }, }, description: "Get Overtimes", }, }, tags: ["Overtime"],})
const getByIdResponseSchema = OvertimeWithRelationsSchema.Schema.omit("deletedAt")
const getByIdDocs = describeRoute({ responses: { 200: { content: { "application/json": { schema: resolver(getByIdResponseSchema), }, }, description: "Get Overtime by OvertimeId", }, 404: { content: { "application/json": { schema: resolver(S.Struct({ message: S.Literal("Not Found").pipe(S.optional, S.withConstructorDefault(() => "Not Found" as const)), })), }, }, description: "Overtime Not Found", }, }, tags: ["Overtime"],})
const validateGetByIdRequest = validator("param", S.Struct({ overtimeId: Branded.OvertimeIdFromString,}))
const getOvertimeByEmployeeIdResponseSchema = OvertimeWithRelationsSchema.SchemaArray
const getOvertimeByEmployeeIdDoc = describeRoute({ responses: { 200: { content: { "application/json": { schema: resolver(getOvertimeByEmployeeIdResponseSchema), }, }, description: "Get Overtime by EmployeeId", }, }, tags: ["Overtime"],})
const getOvertimeByEmployeeIdValidateRequest = validator("param", S.Struct({ employeeId: Branded.EmployeeIdFromString,}))
const getOvertimeByDepartmentNameDoc = describeRoute({ responses: { 200: { content: { "application/json": { schema: resolver(getOvertimeByEmployeeIdResponseSchema), }, }, description: "Get Overtime by EmployeeId", }, }, tags: ["Overtime"],})
const getOvertimeByDepartmentNameValidateRequest = validator("param", S.Struct({ department: EmployeeSchema.Schema.fields.department,}))
const summaryResponseSchema = S.Struct({ hoursWorked: S.Number.annotations({ jsonSchema: { example: 8.5, type: "number" } }),})
const overtimeSummaryForEmployeeDoc = describeRoute({ responses: { 200: { content: { "application/json": { schema: resolver(summaryResponseSchema), }, }, description: "Get Overtime by EmployeeId", }, }, tags: ["Overtime"],})
const validateOvertimeSummaryByEmployeeIdRequest = { param: validator("param", S.Struct({ employeeId: Branded.EmployeeIdFromString, })), query: validator("query", S.Struct({ end: S.Date.annotations({ jsonSchema: { example: "2021-01-02", format: "date", type: "string" } }), start: S.Date.annotations({ jsonSchema: { example: "2021-01-01", format: "date", type: "string" } }), })),}
const overtimeSummaryForDepartmentDoc = describeRoute({ responses: { 200: { content: { "application/json": { schema: resolver(summaryResponseSchema), }, }, description: "Get Overtime by Department", }, }, tags: ["Overtime"],})
const validateOvertimeSummaryByDepartmentRequest = { param: validator("param", S.Struct({ department: EmployeeSchema.Schema.fields.department, })), query: validator("query", S.Struct({ end: S.Date.annotations({ jsonSchema: { example: "2021-01-02", format: "date", type: "string" } }), start: S.Date.annotations({ jsonSchema: { example: "2021-01-01", format: "date", type: "string" } }), })),}
export function setupGet(overtimeService: OvertimeService, employeeService: EmployeeService) { const app = new Hono()
app.get("/", getManyDocs, async (c) => { const overtimes = await overtimeService.findMany() const resObj = Helpers.fromObjectToSchema(getManyResponseSchema)(overtimes)
return c.json(resObj, 200) })
app.get("/:overtimeId", getByIdDocs, validateGetByIdRequest, async (c) => { const { overtimeId } = c.req.valid("param")
const overtime = await overtimeService.findById(overtimeId)
if (overtime === null) return c.json({ message: "Not Found" }, 404)
return c.json(getByIdResponseSchema.make(overtime), 200) })
app.get("/employees/:employeeId", getOvertimeByEmployeeIdDoc, getOvertimeByEmployeeIdValidateRequest, async (c) => { const { employeeId } = c.req.valid("param")
const employeeWithRelations = await employeeService.findOneById(employeeId)
if (employeeWithRelations === null) return c.json({ message: "Not Found" }, 404)
return c.json(Helpers.fromObjectToSchema(getOvertimeByEmployeeIdResponseSchema)(employeeWithRelations.overtimes), 200) })
app.get("/department/:department", getOvertimeByDepartmentNameDoc, getOvertimeByDepartmentNameValidateRequest, async (c) => { const { department } = c.req.valid("param") const overtimes = await overtimeService.findByDepartmentName(department)
const resObj = Helpers.fromObjectToSchema(getOvertimeByEmployeeIdResponseSchema)(overtimes) return c.json(resObj, 200) })
app.get("/employees/:employeeId/summary", overtimeSummaryForEmployeeDoc, validateOvertimeSummaryByEmployeeIdRequest.param, validateOvertimeSummaryByEmployeeIdRequest.query, async (c) => { const { employeeId } = c.req.valid("param") const { end, start } = c.req.valid("query")
const ot = await overtimeService.summaryByEmployeeId({ dateRange: { end, start }, employeeId, }) return c.json(summaryResponseSchema.make(ot), 200) })
app.get("/department/:department/summary", overtimeSummaryForDepartmentDoc, validateOvertimeSummaryByDepartmentRequest.param, validateOvertimeSummaryByDepartmentRequest.query, async (c) => { const { department } = c.req.valid("param") const { end, start } = c.req.valid("query")
const ot = await overtimeService.summaryByDepartment({ dateRange: { end, start }, department, }) return c.json(summaryResponseSchema.make(ot), 200) })
return app}
PUT and DELETE
ไม่ได้มีใน requirements ใครจะทำก็ลอง implement ดูได้นะ
exports controllers
เราจะมาทำ function ที่เอา Routes มารวมกัน แบบนี้
src/controllers/overtimes/index.ts
import type { EmployeeService } from "../../types/services/employee.js"import type { OvertimeService } from "../../types/services/overtime.js"import { Hono } from "hono"import * as Gets from "./get.js"import * as Posts from "./post.js"
export function setupOvertimesRoutes(overtimeService: OvertimeService, employeeService: EmployeeService) { const app = new Hono()
app.route("/", Posts.setupPosts(overtimeService))
app.route("/", Gets.setupGet(overtimeService, employeeService))
return app}
assign group to overtime controllers
src/index.ts
32 collapsed lines
import { config } from "@dotenvx/dotenvx"import { serve } from "@hono/node-server"import { Hono } from "hono"import { setupOpenApi } from "./configure/openapi/setup-openapi.js"import { setupScalarDocs } from "./configure/openapi/setup-scalar-docs.js"import * as EmployeeControllers from "./controllers/employees/index.js"import healthzApp from "./controllers/healthz.js"import * as OvertimeControllers from "./controllers/overtimes/index.js"import initEmployeeRepository from "./repositories/employees/index.js"import { initOvertimeRepository } from "./repositories/overtimes/index.js"import prismaClient from "./repositories/prisma.js"
import { initEmployeeService } from "./services/employee/index.js"import { initOvertimeService } from "./services/overtime/index.js"
config()
const app = new Hono()setupOpenApi(app)
app.route("/docs", setupScalarDocs())
app.route("/healthz", healthzApp)
const employeeRepository = initEmployeeRepository(prismaClient)const employeeService = initEmployeeService(employeeRepository)app.route("/employees", EmployeeControllers.setupEmployeeRoutes(employeeService))
app.get("/", (c) => { return c.text("Home")})
const overtimeRepository = initOvertimeRepository(prismaClient)const overtimeService = initOvertimeService(overtimeRepository)app.route("/overtimes", OvertimeControllers.setupOvertimesRoutes(overtimeService, employeeService))
7 collapsed lines
const port = 3000console.log(`Server is running on http://localhost:${port}`)
serve({ fetch: app.fetch, port,})
ลองเปิด Scalar ดู
