From 6bc0872879dbb372ee635b86f03a4b593305c4c4 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Sat, 29 Jul 2023 17:18:34 +0900 Subject: [PATCH 01/34] =?UTF-8?q?feat:=20histories=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=84=B0=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit histories 라우터에 추가 --- backend/src/v2/routes.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/v2/routes.ts b/backend/src/v2/routes.ts index 0669ae6b..ab3f804b 100644 --- a/backend/src/v2/routes.ts +++ b/backend/src/v2/routes.ts @@ -3,8 +3,10 @@ import { contract } from '@jiphyeonjeon-42/contracts'; import { initServer } from '@ts-rest/express'; import { reviews } from './reviews/impl'; +import { histories } from './histories/impl'; const s = initServer(); export default s.router(contract, { reviews, + histories }); From 642c3aed1064d8f291f5f9fe9d1d4fba94b9d002 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Sat, 29 Jul 2023 17:19:30 +0900 Subject: [PATCH 02/34] =?UTF-8?q?feat:=20histories=20controller=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit histories controller 추가 --- backend/src/v2/histories/controller.ts | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 backend/src/v2/histories/controller.ts diff --git a/backend/src/v2/histories/controller.ts b/backend/src/v2/histories/controller.ts new file mode 100644 index 00000000..863f9136 --- /dev/null +++ b/backend/src/v2/histories/controller.ts @@ -0,0 +1,27 @@ +import { contract } from '@jiphyeonjeon-42/contracts'; +import { P, match } from 'ts-pattern'; +import { + UnauthorizedError, + HandlerFor, + unauthorized, +} from '../shared'; +import { HistoriesService } from './service'; +import { getHistoriesSearchCondition, getHistoriesUserInfo } from './type'; + +// mkGetHistories +type GetDeps = Pick; +type MkGet = (services: GetDeps) => HandlerFor; +export const mkGetHistories: MkGet = ({ searchHistories }) => + async ({ + query: { + query, who, page, limit, type, + }, req: user, + }) => { + const userInfo = getHistoriesUserInfo.safeParse(user); + const parsedQuery = getHistoriesSearchCondition.safeParse(query); + const result = await searchHistories(parsedQuery, userInfo); + + return match(result) + .with(P.instanceOf(UnauthorizedError), () => unauthorized) + .otherwise(() => ({ status: 200, body: '대출 기록이 성공적으로 조회되었습니다.' } as const)); + }; From 012307611c7815d9820598462682aaf6036958c2 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Sat, 29 Jul 2023 17:23:10 +0900 Subject: [PATCH 03/34] =?UTF-8?q?feat:=20impl=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit impl 추가 --- backend/src/v2/histories/impl.ts | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 backend/src/v2/histories/impl.ts diff --git a/backend/src/v2/histories/impl.ts b/backend/src/v2/histories/impl.ts new file mode 100644 index 00000000..2ab1de2e --- /dev/null +++ b/backend/src/v2/histories/impl.ts @@ -0,0 +1,40 @@ +import { contract } from '@jiphyeonjeon-42/contracts'; +import { initServer } from '@ts-rest/express'; +import jipDataSource from '~/app-data-source'; +import BookInfo from '~/entity/entities/BookInfo'; +// import Histories from '~/entity/entities/Histories'; +import { roleSet } from '~/v1/auth/auth.type'; +import authValidate from '~/v1/auth/auth.validate'; + +import { Repository } from 'typeorm'; +import VHistories from '~/entity/entities/VHistories'; + +import { mkGetHistories } from './controller'; +import { + HistoriesService, + MkSearchHistories, +} from './service'; + +const implHistoriesService = (repos: { + histories: Repository; +}) => ({ + searchHistories: MkSearchHistories(repos), +}); + +const implHistoriesController = (service: HistoriesService) => ({ + get: mkGetHistories(service), +}); + +const service = implHistoriesService({ + histories: jipDataSource.getRepository(VHistories), +}); + +const handler = implHistoriesController(service); + +const s = initServer(); +export const histories = s.router(contract.histories, { + get: { + middleware: [authValidate(roleSet.all)], + handler: handler.get, + }, +}); From ae75767ffa29799b8691b2425d6edd193082b046 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Sat, 29 Jul 2023 17:23:42 +0900 Subject: [PATCH 04/34] =?UTF-8?q?feat:=20histories=20=EC=A0=84=EC=9A=A9=20?= =?UTF-8?q?type=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit histories 전용 type 파일 추가 --- backend/src/v2/histories/type.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 backend/src/v2/histories/type.ts diff --git a/backend/src/v2/histories/type.ts b/backend/src/v2/histories/type.ts new file mode 100644 index 00000000..9ad231f7 --- /dev/null +++ b/backend/src/v2/histories/type.ts @@ -0,0 +1,17 @@ +import { z } from 'zod'; +import { positiveInt } from '~/v1/reviews/controller/reviews.type'; + +export type ParsedHistoriesSearchCondition = z.infer; +export const getHistoriesSearchCondition = z.object({ + query: z.string(), + who: z.enum(['my', 'all']), + page: positiveInt.default(0), + limit: positiveInt.default(10), + type: z.enum(['', 'user', 'title', 'callsign', 'bookId']), +}); + +export type ParsedHistoriesUserInfo = z.infer; +export const getHistoriesUserInfo = z.object({ + userId: positiveInt, + userRole: positiveInt.max(3), +}); From a03eb432494358e8abc2434fac21e6b19679ee9f Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Sat, 29 Jul 2023 17:24:07 +0900 Subject: [PATCH 05/34] =?UTF-8?q?feat:=20histories=20service=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit histories service 추가 --- backend/src/v2/histories/service/service.ts | 53 +++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 backend/src/v2/histories/service/service.ts diff --git a/backend/src/v2/histories/service/service.ts b/backend/src/v2/histories/service/service.ts new file mode 100644 index 00000000..007bb381 --- /dev/null +++ b/backend/src/v2/histories/service/service.ts @@ -0,0 +1,53 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { match } from 'ts-pattern'; + +import { Like, type Repository } from 'typeorm'; +import VHistories from '~/entity/entities/VHistories'; + +import { UnauthorizedError } from '~/v2/shared/errors'; +import User from '~/entity/entities/User'; +import type { HistoriesService } from '.'; +import { Meta } from '~/v2/shared'; + +// HistoriesService + +type Repos = { historiesRepo: Repository, userRepo: Repository }; + +type MkSearchHistories = ( + repos: Repos +) => HistoriesService['search']; + +export const mkSearchHistories: MkSearchHistories = (histories) => async ({ + query, who, page, limit, type, +}, { userId, userRole }) => { + if (who === 'all' && userRole !== 2) { + return (new UnauthorizedError()); + } + let filterQuery: any = {}; + if (who === 'my') { + const user = await histories.userRepo.find({ where: { id: userId } }); + filterQuery.login = user[0].nickname; + } else if (type === 'user') { + filterQuery.login = Like(`%${query}%`); + } else if (type === 'title') { + filterQuery.title = Like(`%${query}%`); + } else { + filterQuery = [ + { login: Like(`%${query}%`) }, + { title: Like(`%${query}%`) }, + ]; + } + const [items, count] = await histories.historiesRepo.findAndCount({ + where: filterQuery, + take: limit, + skip: limit * page, + }); + const meta: Meta = { + totalItems: count, + itemCount: items.length, + itemsPerPage: limit, + totalPages: Math.ceil(count / limit), + currentPage: page + 1, + }; + return { items, meta }; +}; From 88a9052ba0b3c97abe18637b1f8bf9f5c839167a Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Sat, 29 Jul 2023 17:25:04 +0900 Subject: [PATCH 06/34] =?UTF-8?q?feat:=20=EA=B6=8C=ED=95=9C=20=EC=97=86?= =?UTF-8?q?=EC=9D=8C=20=EC=97=90=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 권한 없음 에러 추가 --- backend/src/v2/shared/errors.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/src/v2/shared/errors.ts b/backend/src/v2/shared/errors.ts index bf00a974..5a306e25 100644 --- a/backend/src/v2/shared/errors.ts +++ b/backend/src/v2/shared/errors.ts @@ -5,3 +5,11 @@ export class BookInfoNotFoundError extends Error { super(`개별 도서 정보 (id: ${bookInfoId})를 찾을 수 없습니다`); } } + +export class UnauthorizedError extends Error { + declare readonly _tag: 'UnauthorizedError'; + + constructor() { + super('권한이 없습니다'); + } +} From 3153397ade9babeaee0aadfd90f07e34549e7cb6 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Sat, 29 Jul 2023 17:25:35 +0900 Subject: [PATCH 07/34] =?UTF-8?q?feat:=20=EA=B6=8C=ED=95=9C=20=EC=97=86?= =?UTF-8?q?=EC=9D=8C=20=EC=97=90=EB=9F=AC=20=EC=98=A4=EB=B8=8C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 권한 없음 에러 오브젝트 추가 --- backend/src/v2/shared/responses.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/src/v2/shared/responses.ts b/backend/src/v2/shared/responses.ts index 556ca197..050362dd 100644 --- a/backend/src/v2/shared/responses.ts +++ b/backend/src/v2/shared/responses.ts @@ -1,4 +1,4 @@ -import { bookInfoNotFoundSchema, reviewNotFoundSchema } from '@jiphyeonjeon-42/contracts'; +import { bookInfoNotFoundSchema, reviewNotFoundSchema, unauthorizedSchema } from '@jiphyeonjeon-42/contracts'; import { z } from 'zod'; export const reviewNotFound = { @@ -16,3 +16,11 @@ export const bookInfoNotFound = { description: '검색한 책이 존재하지 않습니다.', } as z.infer, } as const; + +export const unauthorized = { + status: 401, + body: { + code: 'UNAUTHORIZED', + description: '권한이 없습니다.', + } as z.infer, +} as const; From 99b5742c5ff49feda409a58b249812e415e7d31b Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Sat, 29 Jul 2023 17:25:52 +0900 Subject: [PATCH 08/34] =?UTF-8?q?feat:=20meta=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit meta 스키마 추가 --- backend/src/v2/shared/types.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/backend/src/v2/shared/types.ts b/backend/src/v2/shared/types.ts index 1444c1ec..042677b2 100644 --- a/backend/src/v2/shared/types.ts +++ b/backend/src/v2/shared/types.ts @@ -1,4 +1,14 @@ import { AppRoute } from '@ts-rest/core'; import { AppRouteOptions } from '@ts-rest/express'; +import { z } from 'zod'; export type HandlerFor = AppRouteOptions['handler']; + +export type Meta = z.infer; +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(), +}); From e4d6bae9e0a3ec89bedefdddd78609dca29014e8 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:06:28 +0900 Subject: [PATCH 09/34] =?UTF-8?q?style:=20eslint=20comma=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eslint comma 추가 --- backend/src/v2/routes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/v2/routes.ts b/backend/src/v2/routes.ts index ab3f804b..7b85ac62 100644 --- a/backend/src/v2/routes.ts +++ b/backend/src/v2/routes.ts @@ -8,5 +8,5 @@ import { histories } from './histories/impl'; const s = initServer(); export default s.router(contract, { reviews, - histories + histories, }); From b29e417b4817b6ff6981eaf9ef39135a25efecdd Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:06:57 +0900 Subject: [PATCH 10/34] =?UTF-8?q?feat:=20contract=EC=97=90=20histories=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit contract에 histories 추가 --- contracts/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/src/index.ts b/contracts/src/index.ts index f542c46d..c8c07556 100644 --- a/contracts/src/index.ts +++ b/contracts/src/index.ts @@ -1,5 +1,6 @@ import { initContract } from '@ts-rest/core'; import { reviewsContract } from './reviews'; +import { historiesContract } from './histories'; import { likesContract } from './likes'; export * from './reviews'; @@ -12,6 +13,8 @@ export const contract = c.router( { // likes: likesContract, reviews: reviewsContract, + histories: historiesContract, + }, { pathPrefix: '/api/v2', From 44575182a9a250e271f787ecbc8e02299d36d4fd Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:07:35 +0900 Subject: [PATCH 11/34] =?UTF-8?q?feat:=20contract=EC=97=90=20histories=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit contract에 histories 명세 추가 --- contracts/src/histories/index.ts | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 contracts/src/histories/index.ts diff --git a/contracts/src/histories/index.ts b/contracts/src/histories/index.ts new file mode 100644 index 00000000..42aec232 --- /dev/null +++ b/contracts/src/histories/index.ts @@ -0,0 +1,37 @@ +import { initContract } from '@ts-rest/core'; +import { z } from 'zod'; +import { + historiesGetQuerySchema, +} from './schema'; +import { unauthorizedSchema } from '../shared'; + +export * from './schema'; + +// contract 를 생성할 때, router 함수를 사용하여 api 를 생성 +const c = initContract(); + +export const historiesContract = c.router( + { + getMyHistories: { + method: 'GET', + path: '/mypage/histories', + description: '마이페이지에서 본인의 대출 기록을 가져온다.', + query: historiesGetQuerySchema, + responses: { + 200: z.literal('대출 기록이 성공적으로 조회되었습니다.'), + 401: unauthorizedSchema, + }, + }, + getAllHistories: { + method: 'GET', + path: '/histories', + description: '사서가 전체 대출 기록을 가져온다.', + query: historiesGetQuerySchema, + responses: { + 200: z.literal('대출 기록이 성공적으로 조회되었습니다.'), + 401: unauthorizedSchema, + }, + }, + + }, +); From 399233b48c9078ae5cf879f3867cdb93250ba491 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:08:19 +0900 Subject: [PATCH 12/34] =?UTF-8?q?feat:=20contract=EC=97=90=20histories?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20que?= =?UTF-8?q?ry=20string=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit contract에 histories에서 사용하는 GET 메서드들의 query string 스키마 추가 --- contracts/src/histories/schema.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 contracts/src/histories/schema.ts diff --git a/contracts/src/histories/schema.ts b/contracts/src/histories/schema.ts new file mode 100644 index 00000000..0fd274ad --- /dev/null +++ b/contracts/src/histories/schema.ts @@ -0,0 +1,9 @@ +import { mkErrorMessageSchema, positiveInt } from '../shared'; +import { z } from '../zodWithOpenapi'; + +export const historiesGetQuerySchema = z.object({ + query: z.string().optional(), + type: z.enum(['user', 'title', 'callsign']).optional(), + page: z.number().int().nonnegative().default(0), + limit: z.number().int().nonnegative().default(10), +}); From 7c4ad3cddbdeb21967eb88f7f5b89f01a531e3ab Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:09:01 +0900 Subject: [PATCH 13/34] =?UTF-8?q?feat:=20contract=EC=97=90=20=EA=B3=B5?= =?UTF-8?q?=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EC=82=AC=EC=9A=A9=ED=95=A0=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EC=97=86=EC=9D=8C=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=8A=A4=ED=82=A4=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit contract에 공용으로 사용할 권한 없음 에러 스키마 추가 --- contracts/src/shared.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/src/shared.ts b/contracts/src/shared.ts index 57ba84da..f7b5f798 100644 --- a/contracts/src/shared.ts +++ b/contracts/src/shared.ts @@ -24,3 +24,7 @@ export const mkErrorMessageSchema = (code: T) => export const bookInfoNotFoundSchema = mkErrorMessageSchema('BOOK_INFO_NOT_FOUND').describe('해당 도서 연관 정보가 존재하지 않습니다'); + +export const unauthorizedSchema = mkErrorMessageSchema('UNAUTHORIZED').describe( + '권한이 없습니다.', +); From ba76a8d64d31a026231bf1eac8979cf1929680fc Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:11:59 +0900 Subject: [PATCH 14/34] =?UTF-8?q?feat(histories):=20histories=20controller?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 전체 대출 기록 조회하는 controller 추가 - 나의 대출 기록 조회하는 controller 추가 --- backend/src/v2/histories/controller.ts | 49 ++++++++++++++++++++------ 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/backend/src/v2/histories/controller.ts b/backend/src/v2/histories/controller.ts index 863f9136..89839d27 100644 --- a/backend/src/v2/histories/controller.ts +++ b/backend/src/v2/histories/controller.ts @@ -6,22 +6,49 @@ import { unauthorized, } from '../shared'; import { HistoriesService } from './service'; -import { getHistoriesSearchCondition, getHistoriesUserInfo } from './type'; -// mkGetHistories -type GetDeps = Pick; -type MkGet = (services: GetDeps) => HandlerFor; -export const mkGetHistories: MkGet = ({ searchHistories }) => +// mkGetMyHistories +type GetMyDeps = Pick; +type MkGetMy = (services: GetMyDeps) => HandlerFor; +export const mkGetMyHistories: MkGetMy = ({ searchMyHistories }) => async ({ query: { - query, who, page, limit, type, - }, req: user, + query, page, limit, type, + }, }) => { - const userInfo = getHistoriesUserInfo.safeParse(user); - const parsedQuery = getHistoriesSearchCondition.safeParse(query); - const result = await searchHistories(parsedQuery, userInfo); + contract.histories.getMyHistories.query.safeParse({ + query, page, limit, type, + }); + const result = await searchMyHistories({ + query, page, limit, type, + }); return match(result) .with(P.instanceOf(UnauthorizedError), () => unauthorized) - .otherwise(() => ({ status: 200, body: '대출 기록이 성공적으로 조회되었습니다.' } as const)); + .otherwise(() => ({ + status: 200, + body: contract.histories.getMyHistories.responses[200].value, + } as const)); + }; + +// mkGetAllHistories +type GetAllDeps = Pick; +type MkGetAll = (services: GetAllDeps) => HandlerFor; +export const mkGetAllHistories: MkGetAll = ({ searchAllHistories }) => + async ({ + query: { + query, page, limit, type, + }, + }) => { + const parsedQuery = contract.histories.getMyHistories.query.parse({ + query, page, limit, type, + }); + const result = await searchAllHistories(parsedQuery); + + return match(result) + .with(P.instanceOf(UnauthorizedError), () => unauthorized) + .otherwise(() => ({ + status: 200, + body: contract.histories.getAllHistories.responses[200].value, + } as const)); }; From 37080d6802a7b0bc3a273ba4eb638126afd2cf94 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:12:21 +0900 Subject: [PATCH 15/34] =?UTF-8?q?feat(histories):=20histories=20service=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 전체 대출 기록 조회하는 service 추가 - 나의 대출 기록 조회하는 service 추가 --- backend/src/v2/histories/service/service.ts | 43 ++++++++++++--------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/backend/src/v2/histories/service/service.ts b/backend/src/v2/histories/service/service.ts index 007bb381..c47873de 100644 --- a/backend/src/v2/histories/service/service.ts +++ b/backend/src/v2/histories/service/service.ts @@ -1,41 +1,46 @@ /* eslint-disable import/no-extraneous-dependencies */ import { match } from 'ts-pattern'; -import { Like, type Repository } from 'typeorm'; +import { FindOperator, Like, type Repository } from 'typeorm'; import VHistories from '~/entity/entities/VHistories'; import { UnauthorizedError } from '~/v2/shared/errors'; import User from '~/entity/entities/User'; -import type { HistoriesService } from '.'; import { Meta } from '~/v2/shared'; +import type { HistoriesService } from '.'; // HistoriesService -type Repos = { historiesRepo: Repository, userRepo: Repository }; +type Repos = { historiesRepo: Repository }; type MkSearchHistories = ( repos: Repos -) => HistoriesService['search']; +) => HistoriesService['searchAllHistories']; + +type whereCondition = { + login: FindOperator, + title: FindOperator, +} | [{ + login: FindOperator, + title: FindOperator, +}]; export const mkSearchHistories: MkSearchHistories = (histories) => async ({ - query, who, page, limit, type, -}, { userId, userRole }) => { - if (who === 'all' && userRole !== 2) { - return (new UnauthorizedError()); - } - let filterQuery: any = {}; - if (who === 'my') { - const user = await histories.userRepo.find({ where: { id: userId } }); - filterQuery.login = user[0].nickname; - } else if (type === 'user') { + query, type, page, limit, +}) => { + let filterQuery: whereCondition = { + login: Like('%%'), + title: Like('%%'), + }; + if (type === 'user') { filterQuery.login = Like(`%${query}%`); } else if (type === 'title') { filterQuery.title = Like(`%${query}%`); } else { - filterQuery = [ - { login: Like(`%${query}%`) }, - { title: Like(`%${query}%`) }, - ]; + filterQuery = [{ + login: Like(`%${query}%`), + title: Like(`%${query}%`), + }]; } const [items, count] = await histories.historiesRepo.findAndCount({ where: filterQuery, @@ -49,5 +54,5 @@ export const mkSearchHistories: MkSearchHistories = (histories) => async ({ totalPages: Math.ceil(count / limit), currentPage: page + 1, }; - return { items, meta }; + return [items, meta]; }; From 3df6b8de092dae85677f662eabf0bc2e44074a76 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:13:31 +0900 Subject: [PATCH 16/34] =?UTF-8?q?feat(histories):=20histories=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=8B=9C=EC=9D=98=20query=20string=20schema=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit histories 조회 시의 query string schema 구조 변경 --- backend/src/v2/histories/type.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/src/v2/histories/type.ts b/backend/src/v2/histories/type.ts index 9ad231f7..6cfcd292 100644 --- a/backend/src/v2/histories/type.ts +++ b/backend/src/v2/histories/type.ts @@ -3,11 +3,10 @@ import { positiveInt } from '~/v1/reviews/controller/reviews.type'; export type ParsedHistoriesSearchCondition = z.infer; export const getHistoriesSearchCondition = z.object({ - query: z.string(), - who: z.enum(['my', 'all']), - page: positiveInt.default(0), - limit: positiveInt.default(10), - type: z.enum(['', 'user', 'title', 'callsign', 'bookId']), + query: z.string().optional(), + type: z.enum(['user', 'title', 'callsign']).optional(), + page: z.number().nonnegative().default(0), + limit: z.number().nonnegative().default(10), }); export type ParsedHistoriesUserInfo = z.infer; From dd774feaeac374d5ec2a8ff285e2e627ebb96714 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:14:34 +0900 Subject: [PATCH 17/34] =?UTF-8?q?feat(histories):=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=84=B0=20=EB=B6=84=EA=B8=B0=20=EC=9E=90=EC=84=B8=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EB=82=98=EB=88=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - histories의 라우터 분기를 '나의 기록 조회', '전체 기록 조회'로 나눔 --- backend/src/v2/histories/impl.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/backend/src/v2/histories/impl.ts b/backend/src/v2/histories/impl.ts index 2ab1de2e..306524a3 100644 --- a/backend/src/v2/histories/impl.ts +++ b/backend/src/v2/histories/impl.ts @@ -9,32 +9,38 @@ import authValidate from '~/v1/auth/auth.validate'; import { Repository } from 'typeorm'; import VHistories from '~/entity/entities/VHistories'; -import { mkGetHistories } from './controller'; +import { mkGetMyHistories, mkGetAllHistories } from './controller'; import { HistoriesService, - MkSearchHistories, + mkSearchHistories, } from './service'; const implHistoriesService = (repos: { - histories: Repository; + historiesRepo: Repository; }) => ({ - searchHistories: MkSearchHistories(repos), + searchMyHistories: mkSearchHistories(repos), + searchAllHistories: mkSearchHistories(repos), }); const implHistoriesController = (service: HistoriesService) => ({ - get: mkGetHistories(service), + getMyHistories: mkGetMyHistories(service), + getAllHistories: mkGetAllHistories(service), }); const service = implHistoriesService({ - histories: jipDataSource.getRepository(VHistories), + historiesRepo: jipDataSource.getRepository(VHistories), }); const handler = implHistoriesController(service); const s = initServer(); export const histories = s.router(contract.histories, { - get: { + getMyHistories: { middleware: [authValidate(roleSet.all)], - handler: handler.get, + handler: handler.getMyHistories, + }, + getAllHistories: { + middleware: [authValidate(roleSet.librarian)], + handler: handler.getAllHistories, }, }); From 92d72deb7320bbed4c7640f56d0bd5bc6837bc9e Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:20:00 +0900 Subject: [PATCH 18/34] =?UTF-8?q?feat(histories):=20histories=20service=20?= =?UTF-8?q?index.ts=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit histories service index.ts 추가 --- backend/src/v2/histories/service/index.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 backend/src/v2/histories/service/index.ts diff --git a/backend/src/v2/histories/service/index.ts b/backend/src/v2/histories/service/index.ts new file mode 100644 index 00000000..89957f6b --- /dev/null +++ b/backend/src/v2/histories/service/index.ts @@ -0,0 +1,20 @@ +import VHistories from '~/entity/entities/VHistories'; +import { Meta, UnauthorizedError } from '~/v2/shared'; + +type Args = { + query?: string | undefined; + page: number; + limit: number; + type?: 'title' | 'user' | 'callsign' | undefined; +}; + +export type HistoriesService = { + searchMyHistories: ( + args: Args, + ) => Promise; + searchAllHistories: ( + args: Args, + ) => Promise; + } + +export * from './service'; From ec427dfecd6098f34bf03de97109c2f5cd7fba0f Mon Sep 17 00:00:00 2001 From: scarf Date: Wed, 2 Aug 2023 17:26:22 +0900 Subject: [PATCH 19/34] =?UTF-8?q?fix:=20=EC=BF=BC=EB=A6=AC=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=EB=A5=BC=20json=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=ED=8C=8C=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 참고: https://ts-rest.com/docs/core/#query-parameters https://ts-rest.com/docs/express/#options --- backend/src/app.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/app.ts b/backend/src/app.ts index f58c9a4b..aa7be1de 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -81,6 +81,7 @@ app.use('/api', router); createExpressEndpoints(contract, routerV2, app, { logInitialization: true, responseValidation: true, + jsonQuery: true, }); // 에러 핸들러 From 96894cb05e649b9779443527600126c91eb3a28f Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:32:46 +0900 Subject: [PATCH 20/34] =?UTF-8?q?feat(histories):=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=20=EC=8B=9C,=20literal=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 결과 반환 시, z.literal 조회 성공 문구 대신 데이터베이스 조회한 결과 반환하게 함 --- backend/src/v2/histories/controller.ts | 37 ++++++++++++-------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/backend/src/v2/histories/controller.ts b/backend/src/v2/histories/controller.ts index 89839d27..f0cf8cc6 100644 --- a/backend/src/v2/histories/controller.ts +++ b/backend/src/v2/histories/controller.ts @@ -3,9 +3,10 @@ import { P, match } from 'ts-pattern'; import { UnauthorizedError, HandlerFor, - unauthorized, + unauthorized, Meta, } from '../shared'; import { HistoriesService } from './service'; +import { VHistories } from "~/entity/entities"; // mkGetMyHistories type GetMyDeps = Pick; @@ -27,28 +28,24 @@ export const mkGetMyHistories: MkGetMy = ({ searchMyHistories }) => .with(P.instanceOf(UnauthorizedError), () => unauthorized) .otherwise(() => ({ status: 200, - body: contract.histories.getMyHistories.responses[200].value, + body: contract.histories.getMyHistories.responses[200], } as const)); }; // mkGetAllHistories type GetAllDeps = Pick; type MkGetAll = (services: GetAllDeps) => HandlerFor; -export const mkGetAllHistories: MkGetAll = ({ searchAllHistories }) => - async ({ - query: { - query, page, limit, type, - }, - }) => { - const parsedQuery = contract.histories.getMyHistories.query.parse({ - query, page, limit, type, - }); - const result = await searchAllHistories(parsedQuery); - - return match(result) - .with(P.instanceOf(UnauthorizedError), () => unauthorized) - .otherwise(() => ({ - status: 200, - body: contract.histories.getAllHistories.responses[200].value, - } as const)); - }; +export const mkGetAllHistories: MkGetAll = ({ searchAllHistories }) => async ({ + query: { query, page, limit, type }, +}) => { + const parsedQuery = contract.histories.getMyHistories.query.parse({ + query, page, limit, type, + }); + const result = await searchAllHistories(parsedQuery); + return match(result) + .with(P.instanceOf(UnauthorizedError), () => unauthorized) + .otherwise(() => ({ + status: 200, + body: result, + } as const)); +}; From 1d956efdae05c021facc5e0c8708d330638c380f Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:34:01 +0900 Subject: [PATCH 21/34] =?UTF-8?q?style(histories):=20import=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VHistories 가져올 때 중괄호 이용 --- backend/src/v2/histories/impl.ts | 4 +--- backend/src/v2/histories/service/index.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/src/v2/histories/impl.ts b/backend/src/v2/histories/impl.ts index 306524a3..881941d3 100644 --- a/backend/src/v2/histories/impl.ts +++ b/backend/src/v2/histories/impl.ts @@ -1,13 +1,11 @@ import { contract } from '@jiphyeonjeon-42/contracts'; import { initServer } from '@ts-rest/express'; import jipDataSource from '~/app-data-source'; -import BookInfo from '~/entity/entities/BookInfo'; -// import Histories from '~/entity/entities/Histories'; import { roleSet } from '~/v1/auth/auth.type'; import authValidate from '~/v1/auth/auth.validate'; import { Repository } from 'typeorm'; -import VHistories from '~/entity/entities/VHistories'; +import { VHistories } from '~/entity/entities/VHistories'; import { mkGetMyHistories, mkGetAllHistories } from './controller'; import { diff --git a/backend/src/v2/histories/service/index.ts b/backend/src/v2/histories/service/index.ts index 89957f6b..5a396683 100644 --- a/backend/src/v2/histories/service/index.ts +++ b/backend/src/v2/histories/service/index.ts @@ -1,4 +1,4 @@ -import VHistories from '~/entity/entities/VHistories'; +import { VHistories } from '~/entity/entities/VHistories'; import { Meta, UnauthorizedError } from '~/v2/shared'; type Args = { From ebc2df1ee2413ca5d14fec17cd0adec3969cc446 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:34:50 +0900 Subject: [PATCH 22/34] =?UTF-8?q?feat(histories):=20histories=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B2=B0=EA=B3=BC=20200=20=EC=8B=9C,=20=EC=8A=A4?= =?UTF-8?q?=ED=82=A4=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit histories를 조회하여 200 스테이터스 일 때 반환하는 스키마 추가 --- contracts/src/histories/index.ts | 6 +++--- contracts/src/histories/schema.ts | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/contracts/src/histories/index.ts b/contracts/src/histories/index.ts index 42aec232..ea92ccc4 100644 --- a/contracts/src/histories/index.ts +++ b/contracts/src/histories/index.ts @@ -1,7 +1,7 @@ import { initContract } from '@ts-rest/core'; import { z } from 'zod'; import { - historiesGetQuerySchema, + historiesGetQuerySchema, historiesGetResponseSchema, } from './schema'; import { unauthorizedSchema } from '../shared'; @@ -18,7 +18,7 @@ export const historiesContract = c.router( description: '마이페이지에서 본인의 대출 기록을 가져온다.', query: historiesGetQuerySchema, responses: { - 200: z.literal('대출 기록이 성공적으로 조회되었습니다.'), + 200: historiesGetResponseSchema, 401: unauthorizedSchema, }, }, @@ -28,7 +28,7 @@ export const historiesContract = c.router( description: '사서가 전체 대출 기록을 가져온다.', query: historiesGetQuerySchema, responses: { - 200: z.literal('대출 기록이 성공적으로 조회되었습니다.'), + 200: historiesGetResponseSchema, 401: unauthorizedSchema, }, }, diff --git a/contracts/src/histories/schema.ts b/contracts/src/histories/schema.ts index 0fd274ad..2b6dcd6f 100644 --- a/contracts/src/histories/schema.ts +++ b/contracts/src/histories/schema.ts @@ -1,4 +1,4 @@ -import { mkErrorMessageSchema, positiveInt } from '../shared'; +import { metaSchema, positiveInt } from '../shared'; import { z } from '../zodWithOpenapi'; export const historiesGetQuerySchema = z.object({ @@ -7,3 +7,26 @@ export const historiesGetQuerySchema = z.object({ page: z.number().int().nonnegative().default(0), limit: z.number().int().nonnegative().default(10), }); + +export const historiesGetResponseSchema = z.object({ + items: z.array( + z.object({ + id: positiveInt, + lendingCondition: z.string(), + login: z.string(), + returningCondition: z.string(), + penaltyDays: z.number().int().nonnegative(), + callSign: z.string(), + title: z.string(), + bookInfoId: positiveInt, + image: z.string(), + createdAt: z.string(), + returnedAt: z.string(), + updatedAt: z.date(), + dueDate: z.string(), + lendingLibrarianNickName: z.string(), + returningLibrarianNickname: z.string(), + }), + ), + meta: metaSchema, +}); From 287619399c4368b0d5779c07b37f9dc2a78b1b78 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:35:54 +0900 Subject: [PATCH 23/34] =?UTF-8?q?feat(histories):=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20callsign=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit callsign으로도 검색할 수 있도록 조건 추가 및 query undefined 체크 추가 --- backend/src/v2/histories/service/service.ts | 48 +++++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/backend/src/v2/histories/service/service.ts b/backend/src/v2/histories/service/service.ts index c47873de..84497baa 100644 --- a/backend/src/v2/histories/service/service.ts +++ b/backend/src/v2/histories/service/service.ts @@ -2,10 +2,8 @@ import { match } from 'ts-pattern'; import { FindOperator, Like, type Repository } from 'typeorm'; -import VHistories from '~/entity/entities/VHistories'; +import { VHistories } from '~/entity/entities/VHistories'; -import { UnauthorizedError } from '~/v2/shared/errors'; -import User from '~/entity/entities/User'; import { Meta } from '~/v2/shared'; import type { HistoriesService } from '.'; @@ -20,29 +18,37 @@ type MkSearchHistories = ( type whereCondition = { login: FindOperator, title: FindOperator, -} | [{ - login: FindOperator, - title: FindOperator, -}]; + callSign: FindOperator, +} | [ + { login: FindOperator }, + { title: FindOperator }, + { callSign: FindOperator }, +]; -export const mkSearchHistories: MkSearchHistories = (histories) => async ({ +export const mkSearchHistories: MkSearchHistories = ({ historiesRepo }) => async ({ query, type, page, limit, }) => { let filterQuery: whereCondition = { login: Like('%%'), title: Like('%%'), + callSign: Like('%%'), }; - if (type === 'user') { - filterQuery.login = Like(`%${query}%`); - } else if (type === 'title') { - filterQuery.title = Like(`%${query}%`); - } else { - filterQuery = [{ - login: Like(`%${query}%`), - title: Like(`%${query}%`), - }]; + if (query !== undefined) { + if (type === 'user') { + filterQuery.login = Like(`%${query}%`); + } else if (type === 'title') { + filterQuery.title = Like(`%${query}%`); + } else if (type === 'callsign') { + filterQuery.callSign = Like(`%${query}%`); + } else { + filterQuery = [ + { login: Like(`%${query}%`) }, + { title: Like(`%${query}%`) }, + { callSign: Like(`%${query}%`) }, + ]; + } } - const [items, count] = await histories.historiesRepo.findAndCount({ + const [items, count] = await historiesRepo.findAndCount({ where: filterQuery, take: limit, skip: limit * page, @@ -54,5 +60,9 @@ export const mkSearchHistories: MkSearchHistories = (histories) => async ({ totalPages: Math.ceil(count / limit), currentPage: page + 1, }; - return [items, meta]; + const returnObject = { + items, + meta, + }; + return returnObject; }; From a90eb0c5e8da1a1ad77c06a9950850250f613cd0 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:36:21 +0900 Subject: [PATCH 24/34] =?UTF-8?q?feat(histories):=20meta=20=EC=8A=A4?= =?UTF-8?q?=ED=82=A4=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit meta 스키마 공용 스키마로 추가 --- contracts/src/shared.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contracts/src/shared.ts b/contracts/src/shared.ts index f7b5f798..4afb920c 100644 --- a/contracts/src/shared.ts +++ b/contracts/src/shared.ts @@ -28,3 +28,11 @@ export const bookInfoNotFoundSchema = export const unauthorizedSchema = mkErrorMessageSchema('UNAUTHORIZED').describe( '권한이 없습니다.', ); + +export const metaSchema = z.object({ + totalItems: z.number().nonnegative(), + itemCount: z.number().nonnegative(), + itemsPerPage: z.number().nonnegative(), + totalPages: z.number().nonnegative(), + currentPage: z.number().nonnegative(), +}); From b81689cfbdedce6b47472c379426325352724957 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:36:44 +0900 Subject: [PATCH 25/34] =?UTF-8?q?feat(histories):=20id=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit id 타입을 string -> number로 변경 --- backend/src/entity/entities/VHistories.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/entity/entities/VHistories.ts b/backend/src/entity/entities/VHistories.ts index c8927026..843d8250 100644 --- a/backend/src/entity/entities/VHistories.ts +++ b/backend/src/entity/entities/VHistories.ts @@ -28,7 +28,7 @@ import { DataSource, ViewColumn, ViewEntity } from 'typeorm'; }) export class VHistories { @ViewColumn() - id: string; + id: number; @ViewColumn() lendingCondition: string; From 83568758974b1fedb9c81027cdad9c41c563b29c Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:44:05 +0900 Subject: [PATCH 26/34] =?UTF-8?q?feat(histories):=20meta=20=EC=8A=A4?= =?UTF-8?q?=ED=82=A4=EB=A7=88=20positiveInt=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit z.number()...를 positiveInt로 변경 --- contracts/src/shared.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/src/shared.ts b/contracts/src/shared.ts index 4afb920c..cd8a778c 100644 --- a/contracts/src/shared.ts +++ b/contracts/src/shared.ts @@ -30,9 +30,9 @@ export const unauthorizedSchema = mkErrorMessageSchema('UNAUTHORIZED').describe( ); export const metaSchema = z.object({ - totalItems: z.number().nonnegative(), - itemCount: z.number().nonnegative(), - itemsPerPage: z.number().nonnegative(), - totalPages: z.number().nonnegative(), - currentPage: z.number().nonnegative(), + totalItems: positiveInt, + itemCount: positiveInt, + itemsPerPage: positiveInt, + totalPages: positiveInt, + currentPage: positiveInt, }); From 4636b8a105ca084d271f7549f59ac7403530e132 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:03:15 +0900 Subject: [PATCH 27/34] =?UTF-8?q?feat(histories):=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=ED=95=A8=EC=88=98=20=EB=B0=98=ED=99=98=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서비스 함수 반환 타입 오브젝트 형식으로 변경 --- backend/src/v2/histories/service/index.ts | 2 +- backend/src/v2/histories/service/service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/v2/histories/service/index.ts b/backend/src/v2/histories/service/index.ts index 5a396683..1d956cff 100644 --- a/backend/src/v2/histories/service/index.ts +++ b/backend/src/v2/histories/service/index.ts @@ -14,7 +14,7 @@ export type HistoriesService = { ) => Promise; searchAllHistories: ( args: Args, - ) => Promise; + ) => Promise<{ items: VHistories[], meta: Meta }>; } export * from './service'; diff --git a/backend/src/v2/histories/service/service.ts b/backend/src/v2/histories/service/service.ts index 84497baa..7d82defc 100644 --- a/backend/src/v2/histories/service/service.ts +++ b/backend/src/v2/histories/service/service.ts @@ -27,7 +27,7 @@ type whereCondition = { export const mkSearchHistories: MkSearchHistories = ({ historiesRepo }) => async ({ query, type, page, limit, -}) => { +}): Promise<{ items: VHistories[], meta: Meta }> => { let filterQuery: whereCondition = { login: Like('%%'), title: Like('%%'), From a91fea2cc226768108b74d5172fd26fc6b5e6f86 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:11:33 +0900 Subject: [PATCH 28/34] =?UTF-8?q?feat(histories):=20getMyHistories=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getMyHistories 반환값 데이터베이스 조회 결과로 변경 --- backend/src/v2/histories/controller.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/v2/histories/controller.ts b/backend/src/v2/histories/controller.ts index f0cf8cc6..86687ad7 100644 --- a/backend/src/v2/histories/controller.ts +++ b/backend/src/v2/histories/controller.ts @@ -28,7 +28,9 @@ export const mkGetMyHistories: MkGetMy = ({ searchMyHistories }) => .with(P.instanceOf(UnauthorizedError), () => unauthorized) .otherwise(() => ({ status: 200, - body: contract.histories.getMyHistories.responses[200], + + + body: result, } as const)); }; From 9c5b5604f7ab92957de2b0a106947b6c482aad78 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:12:03 +0900 Subject: [PATCH 29/34] =?UTF-8?q?feat(histories):=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=ED=95=A8=EC=88=98=20=EB=B0=98=ED=99=98=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서비스 함수들 반환 타입 변경 --- backend/src/v2/histories/service/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/v2/histories/service/index.ts b/backend/src/v2/histories/service/index.ts index 1d956cff..529fbca9 100644 --- a/backend/src/v2/histories/service/index.ts +++ b/backend/src/v2/histories/service/index.ts @@ -11,10 +11,10 @@ type Args = { export type HistoriesService = { searchMyHistories: ( args: Args, - ) => Promise; + ) => Promise; searchAllHistories: ( args: Args, - ) => Promise<{ items: VHistories[], meta: Meta }>; + ) => Promise; } export * from './service'; From c2ed99430e2cd05a57302ca75e7f747bd51a15d6 Mon Sep 17 00:00:00 2001 From: yena <50291995+nyj001012@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:25:17 +0900 Subject: [PATCH 30/34] =?UTF-8?q?chore(histories):=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=ED=8C=8C=EC=9D=BC=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F?= =?UTF-8?q?=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서비스 파일과 서비스 구현 파일, 컨트롤러 파일과 컨트롤러 구현 파일 분리 --- .../histories/{ => controller}/controller.ts | 9 +++----- backend/src/v2/histories/controller/impl.ts | 9 ++++++++ backend/src/v2/histories/controller/index.ts | 1 + backend/src/v2/histories/impl.ts | 22 ++----------------- backend/src/v2/histories/service/impl.ts | 13 +++++++++++ 5 files changed, 28 insertions(+), 26 deletions(-) rename backend/src/v2/histories/{ => controller}/controller.ts (91%) create mode 100644 backend/src/v2/histories/controller/impl.ts create mode 100644 backend/src/v2/histories/controller/index.ts create mode 100644 backend/src/v2/histories/service/impl.ts diff --git a/backend/src/v2/histories/controller.ts b/backend/src/v2/histories/controller/controller.ts similarity index 91% rename from backend/src/v2/histories/controller.ts rename to backend/src/v2/histories/controller/controller.ts index 86687ad7..08dfd37f 100644 --- a/backend/src/v2/histories/controller.ts +++ b/backend/src/v2/histories/controller/controller.ts @@ -3,10 +3,9 @@ import { P, match } from 'ts-pattern'; import { UnauthorizedError, HandlerFor, - unauthorized, Meta, -} from '../shared'; -import { HistoriesService } from './service'; -import { VHistories } from "~/entity/entities"; + unauthorized, +} from '../../shared'; +import { HistoriesService } from '../service'; // mkGetMyHistories type GetMyDeps = Pick; @@ -28,8 +27,6 @@ export const mkGetMyHistories: MkGetMy = ({ searchMyHistories }) => .with(P.instanceOf(UnauthorizedError), () => unauthorized) .otherwise(() => ({ status: 200, - - body: result, } as const)); }; diff --git a/backend/src/v2/histories/controller/impl.ts b/backend/src/v2/histories/controller/impl.ts new file mode 100644 index 00000000..0f1d1079 --- /dev/null +++ b/backend/src/v2/histories/controller/impl.ts @@ -0,0 +1,9 @@ +import { mkGetMyHistories, mkGetAllHistories } from './controller'; +import { + HistoriesService, +} from '../service'; + +export const implHistoriesController = (service: HistoriesService) => ({ + getMyHistories: mkGetMyHistories(service), + getAllHistories: mkGetAllHistories(service), +}); diff --git a/backend/src/v2/histories/controller/index.ts b/backend/src/v2/histories/controller/index.ts new file mode 100644 index 00000000..04714030 --- /dev/null +++ b/backend/src/v2/histories/controller/index.ts @@ -0,0 +1 @@ +export * from './controller'; diff --git a/backend/src/v2/histories/impl.ts b/backend/src/v2/histories/impl.ts index 881941d3..f4f6f3d1 100644 --- a/backend/src/v2/histories/impl.ts +++ b/backend/src/v2/histories/impl.ts @@ -3,27 +3,9 @@ import { initServer } from '@ts-rest/express'; import jipDataSource from '~/app-data-source'; import { roleSet } from '~/v1/auth/auth.type'; import authValidate from '~/v1/auth/auth.validate'; - -import { Repository } from 'typeorm'; import { VHistories } from '~/entity/entities/VHistories'; - -import { mkGetMyHistories, mkGetAllHistories } from './controller'; -import { - HistoriesService, - mkSearchHistories, -} from './service'; - -const implHistoriesService = (repos: { - historiesRepo: Repository; -}) => ({ - searchMyHistories: mkSearchHistories(repos), - searchAllHistories: mkSearchHistories(repos), -}); - -const implHistoriesController = (service: HistoriesService) => ({ - getMyHistories: mkGetMyHistories(service), - getAllHistories: mkGetAllHistories(service), -}); +import { implHistoriesService } from '~/v2/histories/service/impl'; +import { implHistoriesController } from '~/v2/histories/controller/impl'; const service = implHistoriesService({ historiesRepo: jipDataSource.getRepository(VHistories), diff --git a/backend/src/v2/histories/service/impl.ts b/backend/src/v2/histories/service/impl.ts new file mode 100644 index 00000000..f3892862 --- /dev/null +++ b/backend/src/v2/histories/service/impl.ts @@ -0,0 +1,13 @@ +import { Repository } from 'typeorm'; +import { VHistories } from '~/entity/entities/VHistories'; + +import { + mkSearchHistories, +} from './service'; + +export const implHistoriesService = (repos: { + historiesRepo: Repository; +}) => ({ + searchMyHistories: mkSearchHistories(repos), + searchAllHistories: mkSearchHistories(repos), +}); From eec4ade628c7bfdd9c9fd2d9b48720f7e823bb3d Mon Sep 17 00:00:00 2001 From: scarf Date: Sun, 13 Aug 2023 12:57:30 +0900 Subject: [PATCH 31/34] =?UTF-8?q?fix:=20=EC=8A=A4=ED=82=A4=EB=A7=88?= =?UTF-8?q?=EC=97=90=EC=84=9C=20date=EC=99=80=20string=20=EB=AA=A8?= =?UTF-8?q?=EB=91=90=20=ED=97=88=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/src/histories/schema.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/src/histories/schema.ts b/contracts/src/histories/schema.ts index 2b6dcd6f..025b1c2a 100644 --- a/contracts/src/histories/schema.ts +++ b/contracts/src/histories/schema.ts @@ -8,6 +8,8 @@ export const historiesGetQuerySchema = z.object({ limit: z.number().int().nonnegative().default(10), }); +const dateLike = z.union([z.date(), z.string()]).transform(String) + export const historiesGetResponseSchema = z.object({ items: z.array( z.object({ @@ -20,10 +22,10 @@ export const historiesGetResponseSchema = z.object({ title: z.string(), bookInfoId: positiveInt, image: z.string(), - createdAt: z.string(), - returnedAt: z.string(), - updatedAt: z.date(), - dueDate: z.string(), + createdAt: dateLike, + returnedAt: dateLike, + updatedAt: dateLike, + dueDate: dateLike, lendingLibrarianNickName: z.string(), returningLibrarianNickname: z.string(), }), From 2aa991f2c9bd2b89ce551300c83af4f01bdfafcc Mon Sep 17 00:00:00 2001 From: scarf Date: Sun, 13 Aug 2023 12:58:53 +0900 Subject: [PATCH 32/34] =?UTF-8?q?fix:=20=ED=8C=A8=ED=84=B4=20=EB=A7=A4?= =?UTF-8?q?=EC=B9=AD=EC=97=90=EC=84=9C=20=ED=83=80=EC=9E=85=EC=9D=B4=20?= =?UTF-8?q?=EC=A2=81=ED=98=80=EC=A7=80=EC=A7=80=20=EC=95=8A=EB=8D=98=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/v2/histories/controller/controller.ts | 32 ++++--------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/backend/src/v2/histories/controller/controller.ts b/backend/src/v2/histories/controller/controller.ts index 08dfd37f..82983aed 100644 --- a/backend/src/v2/histories/controller/controller.ts +++ b/backend/src/v2/histories/controller/controller.ts @@ -11,40 +11,20 @@ import { HistoriesService } from '../service'; type GetMyDeps = Pick; type MkGetMy = (services: GetMyDeps) => HandlerFor; export const mkGetMyHistories: MkGetMy = ({ searchMyHistories }) => - async ({ - query: { - query, page, limit, type, - }, - }) => { - contract.histories.getMyHistories.query.safeParse({ - query, page, limit, type, - }); - const result = await searchMyHistories({ - query, page, limit, type, - }); + async ({ query }) => { + const result = await searchMyHistories(query); return match(result) .with(P.instanceOf(UnauthorizedError), () => unauthorized) - .otherwise(() => ({ - status: 200, - body: result, - } as const)); + .otherwise((body) => ({ status: 200, body } as const)); }; // mkGetAllHistories type GetAllDeps = Pick; type MkGetAll = (services: GetAllDeps) => HandlerFor; -export const mkGetAllHistories: MkGetAll = ({ searchAllHistories }) => async ({ - query: { query, page, limit, type }, -}) => { - const parsedQuery = contract.histories.getMyHistories.query.parse({ - query, page, limit, type, - }); - const result = await searchAllHistories(parsedQuery); +export const mkGetAllHistories: MkGetAll = ({ searchAllHistories }) => async ({ query }) => { + const result = await searchAllHistories(query); return match(result) .with(P.instanceOf(UnauthorizedError), () => unauthorized) - .otherwise(() => ({ - status: 200, - body: result, - } as const)); + .otherwise((body) => ({ status: 200, body } as const)); }; From d32ded49bd267964f8a18508724dcd81fd4d843c Mon Sep 17 00:00:00 2001 From: scarf Date: Sun, 13 Aug 2023 12:59:55 +0900 Subject: [PATCH 33/34] =?UTF-8?q?fix:=20users=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=88=A8=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/index.ts b/contracts/src/index.ts index 1efa947e..361745b8 100644 --- a/contracts/src/index.ts +++ b/contracts/src/index.ts @@ -18,7 +18,7 @@ export const contract = c.router( histories: historiesContract, stock: stockContract, - users: usersContract, +// users: usersContract, TODO: 유저 라우터 작성 }, { pathPrefix: '/api/v2', From 12c264b9e3554bad7a02bd3893ba0ca1a5fcd2de Mon Sep 17 00:00:00 2001 From: scarf Date: Sun, 13 Aug 2023 13:02:55 +0900 Subject: [PATCH 34/34] =?UTF-8?q?fix:=20stocks=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/v2/stock/controller/controller.ts | 11 +++-------- contracts/src/histories/schema.ts | 4 +--- contracts/src/index.ts | 3 ++- contracts/src/shared.ts | 2 ++ contracts/src/stock/schema.ts | 8 ++++---- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/backend/src/v2/stock/controller/controller.ts b/backend/src/v2/stock/controller/controller.ts index 7c05ce9b..887b1489 100644 --- a/backend/src/v2/stock/controller/controller.ts +++ b/backend/src/v2/stock/controller/controller.ts @@ -10,15 +10,10 @@ import { StockService } from '../service'; type GetDeps = Pick; type MkGet = (services: GetDeps) => HandlerFor; export const mkGetStock: MkGet = ({ searchStock }) => - async ({ query: { page, limit } }) => { - contract.stock.get.query.safeParse({ page, limit }); - const result = await searchStock({ page, limit }); + async ({ query }) => { + const result = await searchStock(query); - return match(result) - .otherwise(() => ({ - status: 200, - body: result, - } as const)); + return { status: 200, body: result } as const; }; type PatchDeps = Pick; diff --git a/contracts/src/histories/schema.ts b/contracts/src/histories/schema.ts index 025b1c2a..0132fe15 100644 --- a/contracts/src/histories/schema.ts +++ b/contracts/src/histories/schema.ts @@ -1,4 +1,4 @@ -import { metaSchema, positiveInt } from '../shared'; +import { dateLike, metaSchema, positiveInt } from '../shared'; import { z } from '../zodWithOpenapi'; export const historiesGetQuerySchema = z.object({ @@ -8,8 +8,6 @@ export const historiesGetQuerySchema = z.object({ limit: z.number().int().nonnegative().default(10), }); -const dateLike = z.union([z.date(), z.string()]).transform(String) - export const historiesGetResponseSchema = z.object({ items: z.array( z.object({ diff --git a/contracts/src/index.ts b/contracts/src/index.ts index 361745b8..47556f36 100644 --- a/contracts/src/index.ts +++ b/contracts/src/index.ts @@ -18,7 +18,8 @@ export const contract = c.router( histories: historiesContract, stock: stockContract, -// users: usersContract, TODO: 유저 라우터 작성 + // TODO(@scarf005): 유저 서비스 작성 +// users: usersContract, }, { pathPrefix: '/api/v2', diff --git a/contracts/src/shared.ts b/contracts/src/shared.ts index f065dd3a..edc79c26 100644 --- a/contracts/src/shared.ts +++ b/contracts/src/shared.ts @@ -2,6 +2,8 @@ import { z } from './zodWithOpenapi'; export const positiveInt = z.coerce.number().int().nonnegative(); +export const dateLike = z.union([z.date(), z.string()]).transform(String) + export const bookInfoIdSchema = positiveInt.describe('개별 도서 ID'); type ErrorMessage = { code: string; description: string }; diff --git a/contracts/src/stock/schema.ts b/contracts/src/stock/schema.ts index 4cb4703c..5aa14ce7 100644 --- a/contracts/src/stock/schema.ts +++ b/contracts/src/stock/schema.ts @@ -1,4 +1,4 @@ -import { metaSchema, positiveInt } from '../shared'; +import { dateLike, metaSchema, positiveInt } from '../shared'; import { z } from '../zodWithOpenapi'; export const bookIdSchema = positiveInt.describe('업데이트 할 도서 ID'); @@ -23,15 +23,15 @@ export const stockGetResponseSchema = z.object({ author: z.string(), donator: z.string(), publisher: z.string(), - publishedAt: z.string(), + publishedAt: dateLike, isbn: z.string(), image: z.string(), status: positiveInt, categoryId: positiveInt, callSign: z.string(), category: z.string(), - updatedAt: z.string(), + updatedAt: dateLike, }), ), meta: metaSchema, -}); \ No newline at end of file +});