-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: stock api contract 작성 * feat: search & update stock api refactoring * fix: update 시 VStock 테이블은 not updatable 이기 때문에 Book 레포 추가 * fix: Update contracts/src/stock/schema.ts updatedAt 필드의 값을 Date->string 으로 처리 Co-authored-by: scarf <[email protected]>
- Loading branch information
1 parent
69e1bc5
commit 80f6953
Showing
16 changed files
with
268 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,14 @@ | ||
import { AppRoute } from '@ts-rest/core'; | ||
import { AppRouteOptions } from '@ts-rest/express'; | ||
import { z } from 'zod'; | ||
|
||
export type HandlerFor<T extends AppRoute> = AppRouteOptions<T>['handler']; | ||
|
||
export type Meta = z.infer<typeof meta>; | ||
export const meta = z.object({ | ||
totalItems: z.number().nonnegative(), | ||
itemCount: z.number().nonnegative(), | ||
itemsPerPage: z.number().nonnegative(), | ||
totalPages: z.number().nonnegative(), | ||
currentPage: z.number().nonnegative(), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { contract } from '@jiphyeonjeon-42/contracts'; | ||
import { P, match } from 'ts-pattern'; | ||
import { UpdateResult } from 'typeorm'; | ||
import { | ||
bookNotFound, | ||
HandlerFor, | ||
} from '../../shared'; | ||
import { StockService } from '../service'; | ||
|
||
type GetDeps = Pick<StockService, 'searchStock'>; | ||
type MkGet = (services: GetDeps) => HandlerFor<typeof contract.stock.get>; | ||
export const mkGetStock: MkGet = ({ searchStock }) => | ||
async ({ query: { page, limit } }) => { | ||
contract.stock.get.query.safeParse({ page, limit }); | ||
const result = await searchStock({ page, limit }); | ||
|
||
return match(result) | ||
.otherwise(() => ({ | ||
status: 200, | ||
body: result, | ||
} as const)); | ||
}; | ||
|
||
type PatchDeps = Pick<StockService, 'updateStock'>; | ||
type MkPatch = (services: PatchDeps) => HandlerFor<typeof contract.stock.patch>; | ||
export const mkPatchStock: MkPatch = ({ updateStock }) => | ||
async ({ body: { id } }) => { | ||
contract.stock.patch.body.safeParse({ id }); | ||
const result = await updateStock({ id }); | ||
|
||
return match(result) | ||
.with( | ||
P.instanceOf(UpdateResult), | ||
() => ({ status: 200, body: '재고 상태가 업데이트되었습니다.' } as const), | ||
) | ||
.otherwise(() => bookNotFound); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { mkGetStock, mkPatchStock } from './controller'; | ||
import { | ||
StockService, | ||
} from '../service'; | ||
|
||
export const implStockController = (service: StockService) => ({ | ||
getStock: mkGetStock(service), | ||
patchStock: mkPatchStock(service), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './controller'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { contract } from '@jiphyeonjeon-42/contracts'; | ||
import { initServer } from '@ts-rest/express'; | ||
import jipDataSource from '~/app-data-source'; | ||
import { VStock, Book } from '~/entity/entities'; | ||
import { implStockService } from '~/v2/stock/service/impl'; | ||
import { implStockController } from '~/v2/stock/controller/impl'; | ||
|
||
const service = implStockService({ | ||
stockRepo: jipDataSource.getRepository(VStock), | ||
bookRepo: jipDataSource.getRepository(Book), | ||
}); | ||
|
||
const handler = implStockController(service); | ||
|
||
const s = initServer(); | ||
export const stock = s.router(contract.stock, { | ||
get: { | ||
handler: handler.getStock, | ||
}, | ||
patch: { | ||
handler: handler.patchStock, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Repository } from 'typeorm'; | ||
import { VStock, Book } from '~/entity/entities'; | ||
|
||
import { mkSearchStock, mkUpdateStock } from './service'; | ||
|
||
export const implStockService = (repos: { | ||
stockRepo: Repository<VStock>; | ||
bookRepo: Repository<Book>; | ||
}) => ({ | ||
searchStock: mkSearchStock(repos), | ||
updateStock: mkUpdateStock(repos), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { UpdateResult } from 'typeorm'; | ||
import { VStock } from '~/entity/entities'; | ||
import { Meta, BookNotFoundError } from '~/v2/shared'; | ||
|
||
type SearchArgs = { | ||
page: number, | ||
limit: number, | ||
}; | ||
|
||
type UpdateArgs = { | ||
id: number, | ||
}; | ||
|
||
export type StockService = { | ||
searchStock: ( | ||
args: SearchArgs, | ||
) => Promise<{ items: VStock[], meta: Meta }>; | ||
updateStock: ( | ||
args: UpdateArgs, | ||
) => Promise<BookNotFoundError | UpdateResult>; | ||
}; | ||
|
||
export * from './service'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { match } from 'ts-pattern'; | ||
|
||
import { VStock, Book } from '~/entity/entities'; | ||
import { type Repository, LessThan } from 'typeorm'; | ||
|
||
import { startOfDay, addDays } from 'date-fns'; | ||
import { Meta } from '~/v2/shared'; | ||
import { BookNotFoundError } from '~/v2/shared/errors'; | ||
import type { StockService } from '.'; | ||
|
||
type Repos = { stockRepo: Repository<VStock> }; | ||
|
||
type MkSearchStock = ( | ||
repos: Repos | ||
) => StockService['searchStock']; | ||
|
||
export const mkSearchStock: MkSearchStock = ({ stockRepo }) => | ||
async ({ page, limit }) => { | ||
const today = startOfDay(new Date()); | ||
const [items, totalItems] = await stockRepo.findAndCount({ | ||
where: { | ||
updatedAt: LessThan(addDays(today, -15)), | ||
}, | ||
take: limit, | ||
skip: limit * page, | ||
}); | ||
|
||
const meta: Meta = { | ||
totalItems, | ||
itemCount: items.length, | ||
itemsPerPage: limit, | ||
totalPages: Math.ceil(totalItems / limit), | ||
currentPage: page + 1, | ||
}; | ||
|
||
const returnObject = { | ||
items, | ||
meta, | ||
}; | ||
console.log(returnObject); | ||
return returnObject; | ||
}; | ||
|
||
type MkUpdateStock = ( | ||
repos: Repos & { bookRepo: Repository<Book> }) | ||
=> StockService['updateStock']; | ||
export const mkUpdateStock: MkUpdateStock = ({ stockRepo, bookRepo }) => | ||
async ({ id }) => { | ||
const stock = await stockRepo.findOneBy({ bookId: id }); | ||
|
||
return match(stock) | ||
.with(null, () => new BookNotFoundError(id)) | ||
.otherwise(() => bookRepo.update({ id }, { updatedAt: new Date() })); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { initContract } from '@ts-rest/core'; | ||
import { | ||
stockGetQuerySchema, | ||
stockGetResponseSchema, | ||
stockPatchBodySchema, | ||
stockPatchResponseSchema | ||
} from './schema'; | ||
import { bookNotFoundSchema } from '../shared'; | ||
|
||
const c = initContract(); | ||
|
||
export const stockContract = c.router( | ||
{ | ||
get: { | ||
method: 'GET', | ||
path: '/search', | ||
description: '책 재고 정보를 검색해 온다.', | ||
query: stockGetQuerySchema, | ||
responses: { | ||
200: stockGetResponseSchema, | ||
// 특정한 에러케이스가 생각나지 않습니다. | ||
}, | ||
}, | ||
patch: { | ||
method: 'PATCH', | ||
path: '/update', | ||
description: '책 재고를 확인하고 수정일시를 업데이트한다.', | ||
body: stockPatchBodySchema, | ||
responses: { | ||
200: stockPatchResponseSchema, | ||
404: bookNotFoundSchema, | ||
}, | ||
}, | ||
}, | ||
{ pathPrefix: '/stock'}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { metaSchema, positiveInt } from '../shared'; | ||
import { z } from '../zodWithOpenapi'; | ||
|
||
export const bookIdSchema = positiveInt.describe('업데이트 할 도서 ID'); | ||
|
||
export const stockPatchBodySchema = z.object({ | ||
id: bookIdSchema.openapi({ example: 0 }), | ||
}); | ||
|
||
export const stockPatchResponseSchema = z.literal('재고 상태가 업데이트되었습니다.'); | ||
|
||
export const stockGetQuerySchema = z.object({ | ||
page: positiveInt.default(0), | ||
limit: positiveInt.default(10), | ||
}); | ||
|
||
export const stockGetResponseSchema = z.object({ | ||
items: z.array( | ||
z.object({ | ||
bookId: positiveInt, | ||
bookInfoId: positiveInt, | ||
title: z.string(), | ||
author: z.string(), | ||
donator: z.string(), | ||
publisher: z.string(), | ||
publishedAt: z.string(), | ||
isbn: z.string(), | ||
image: z.string(), | ||
status: positiveInt, | ||
categoryId: positiveInt, | ||
callSign: z.string(), | ||
category: z.string(), | ||
updatedAt: z.string(), | ||
}), | ||
), | ||
meta: metaSchema, | ||
}); |