-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat base repository and fix funding api for executive feedbacks #1419
Changes from all commits
8be35dc
fd47e14
b319180
613444c
7573d74
e814f7d
c9365cb
0b8deac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export abstract class MEntity { | ||
id: number; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import { Inject, Injectable, NotFoundException } from "@nestjs/common"; | ||
import { ColumnBaseConfig, ColumnDataType, eq, inArray } from "drizzle-orm"; | ||
import { MySqlColumn, MySqlTable } from "drizzle-orm/mysql-core"; | ||
import { MySql2Database } from "drizzle-orm/mysql2"; | ||
|
||
import { | ||
DrizzleAsyncProvider, | ||
DrizzleTransaction, | ||
} from "@sparcs-clubs/api/drizzle/drizzle.provider"; | ||
|
||
import { MEntity } from "../model/entity.model"; | ||
import { getKSTDate } from "../util/util"; | ||
|
||
interface TableWithId { | ||
id: MySqlColumn<ColumnBaseConfig<ColumnDataType, string>>; | ||
} | ||
|
||
interface ModelWithFrom<T extends MEntity, D> { | ||
from(result: D): T; | ||
} | ||
|
||
@Injectable() | ||
export abstract class BaseRepository< | ||
M extends MEntity, | ||
R, | ||
D, | ||
T extends MySqlTable & TableWithId, | ||
> { | ||
@Inject(DrizzleAsyncProvider) private db: MySql2Database; | ||
|
||
constructor( | ||
protected table: T, | ||
protected modelClass: ModelWithFrom<M, D>, | ||
) {} | ||
|
||
async withTransaction<Result>( | ||
callback: (tx: DrizzleTransaction) => Promise<Result>, | ||
): Promise<Result> { | ||
return this.db.transaction(callback); | ||
} | ||
|
||
async findTx(tx: DrizzleTransaction, id: number): Promise<M | null> { | ||
const result = await tx | ||
.select() | ||
.from(this.table) | ||
.where(eq(this.table.id, id)) | ||
.then(rows => rows.map(row => this.modelClass.from(row as D))); | ||
|
||
return (result[0] as M) ?? null; | ||
} | ||
|
||
async find(id: number): Promise<M | null> { | ||
return this.withTransaction(async tx => this.findTx(tx, id)); | ||
} | ||
|
||
async fetchTx(tx: DrizzleTransaction, id: number): Promise<M> { | ||
const result = await this.findTx(tx, id); | ||
if (result === null) { | ||
throw new NotFoundException(`Not found: ${this.table} with id: ${id}`); | ||
} | ||
return result; | ||
} | ||
|
||
async fetch(id: number): Promise<M> { | ||
return this.withTransaction(async tx => this.fetchTx(tx, id)); | ||
} | ||
|
||
async fetchAllTx(tx: DrizzleTransaction, ids: number[]): Promise<M[]> { | ||
if (ids.length === 0) { | ||
return []; | ||
} | ||
|
||
const result = await tx | ||
.select() | ||
.from(this.table) | ||
.where(inArray(this.table.id, ids)); | ||
|
||
return result.map(row => this.modelClass.from(row as D)); | ||
} | ||
|
||
async fetchAll(ids: number[]): Promise<M[]> { | ||
return this.withTransaction(async tx => this.fetchAllTx(tx, ids)); | ||
} | ||
|
||
async insertTx(tx: DrizzleTransaction, param: R): Promise<M> { | ||
const [result] = await tx.insert(this.table).values(param).execute(); | ||
|
||
const newId = Number(result.insertId); | ||
|
||
return this.fetchTx(tx, newId); | ||
} | ||
|
||
async insert(param: R): Promise<M> { | ||
return this.withTransaction(async tx => this.insertTx(tx, param)); | ||
} | ||
|
||
async putTx(tx: DrizzleTransaction, id: number, param: R): Promise<M> { | ||
await tx | ||
.update(this.table) | ||
.set(param) | ||
.where(eq(this.table.id, id)) | ||
.execute(); | ||
return this.fetchTx(tx, id); | ||
} | ||
|
||
async put(id: number, param: R): Promise<M> { | ||
return this.withTransaction(async tx => this.putTx(tx, id, param)); | ||
} | ||
|
||
async patchTx( | ||
tx: DrizzleTransaction, | ||
oldbie: M, | ||
consumer: (oldbie: M) => M, | ||
): Promise<M> { | ||
const param = consumer(oldbie); | ||
await tx | ||
.update(this.table) | ||
.set(param) | ||
.where(eq(this.table.id, oldbie.id)) | ||
.execute(); | ||
|
||
return this.fetchTx(tx, oldbie.id); | ||
} | ||
Comment on lines
+110
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. patch의 방향이 Partial 대신에 consumer 로 갔으면 좋겠음. 이렇게 했을 때 메리트는 단순히 필드 하나 바꾸는 동작 이상의 뭔가를 할수 있음. consumer 안에서 조건문을 쓰거나, 모델의 클래스 메서드를 호출하거나 등등 활용성이 아주 좋아짐. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 구상은 이해하는데 실용적으로 사용가능할지 더 사용 필요할듯 |
||
|
||
async patch(oldbie: M, consumer: (oldbie: M) => M): Promise<M> { | ||
return this.withTransaction(async tx => this.patchTx(tx, oldbie, consumer)); | ||
} | ||
|
||
async deleteTx(tx: DrizzleTransaction, id: number): Promise<void> { | ||
await tx | ||
.update(this.table) | ||
.set({ deletedAt: getKSTDate() }) | ||
.where(eq(this.table.id, id)) | ||
.execute(); | ||
} | ||
|
||
async delete(id: number): Promise<void> { | ||
return this.withTransaction(async tx => this.deleteTx(tx, id)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ import { | |
|
||
import { Activity, ActivityD } from "./activity.schema"; | ||
import { Club } from "./club.schema"; | ||
import { ExecutiveT, StudentT } from "./user.schema"; | ||
import { Executive, StudentT } from "./user.schema"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 엥 이거 왜 바꼈죠 |
||
|
||
export const Funding = mysqlTable( | ||
"funding", | ||
|
@@ -97,7 +97,7 @@ export const Funding = mysqlTable( | |
executiveForeignKey: foreignKey({ | ||
name: "funding_charged_executive_id_fk", | ||
columns: [table.chargedExecutiveId], | ||
foreignColumns: [ExecutiveT.id], | ||
foreignColumns: [Executive.id], | ||
}), | ||
}), | ||
); | ||
|
@@ -398,7 +398,7 @@ export const FundingFeedback = mysqlTable( | |
executiveForeignKey: foreignKey({ | ||
name: "funding_feedback_executive_id_fk", | ||
columns: [table.chargedExecutiveId], | ||
foreignColumns: [ExecutiveT.id], | ||
foreignColumns: [Executive.id], | ||
}), | ||
}), | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,71 @@ | ||
import { Inject, Injectable } from "@nestjs/common"; | ||
import { IFundingCommentRequestCreate } from "@sparcs-clubs/interface/api/funding/type/funding.type"; | ||
import { eq } from "drizzle-orm"; | ||
import { Injectable } from "@nestjs/common"; | ||
|
||
import { MySql2Database } from "drizzle-orm/mysql2"; | ||
import { IFundingCommentRequest } from "@sparcs-clubs/interface/api/funding/type/funding.comment.type"; | ||
import { eq } from "drizzle-orm"; | ||
|
||
import { | ||
DrizzleAsyncProvider, | ||
DrizzleTransaction, | ||
} from "@sparcs-clubs/api/drizzle/drizzle.provider"; | ||
import { BaseRepository } from "@sparcs-clubs/api/common/repository/base.repository"; | ||
import { DrizzleTransaction } from "@sparcs-clubs/api/drizzle/drizzle.provider"; | ||
import { FundingFeedback } from "@sparcs-clubs/api/drizzle/schema/funding.schema"; | ||
import { MFundingComment } from "@sparcs-clubs/api/feature/funding/model/funding.comment.model"; | ||
import { | ||
FundingCommentDbResult, | ||
MFundingComment, | ||
} from "@sparcs-clubs/api/feature/funding/model/funding.comment.model"; | ||
|
||
@Injectable() | ||
export default class FundingCommentRepository { | ||
constructor(@Inject(DrizzleAsyncProvider) private db: MySql2Database) {} | ||
|
||
// WARD: Transaction | ||
async withTransaction<T>( | ||
callback: (tx: DrizzleTransaction) => Promise<T>, | ||
): Promise<T> { | ||
return this.db.transaction(callback); | ||
} | ||
|
||
async fetchAll(fundingId: number): Promise<MFundingComment[]> { | ||
return this.db.transaction(async tx => this.fetchAllTx(tx, fundingId)); | ||
export default class FundingCommentRepository extends BaseRepository< | ||
MFundingComment, | ||
IFundingCommentRequest, | ||
FundingCommentDbResult, | ||
typeof FundingFeedback | ||
> { | ||
constructor() { | ||
super(FundingFeedback, MFundingComment); | ||
} | ||
|
||
async fetchAllTx( | ||
tx: DrizzleTransaction, | ||
ids: number[], | ||
): Promise<MFundingComment[]>; | ||
async fetchAllTx( | ||
tx: DrizzleTransaction, | ||
fundingId: number, | ||
): Promise<MFundingComment[]>; | ||
async fetchAllTx( | ||
tx: DrizzleTransaction, | ||
arg1: number | number[], | ||
): Promise<MFundingComment[]> { | ||
if (Array.isArray(arg1)) { | ||
return super.fetchAllTx(tx, arg1); | ||
} | ||
|
||
const result = await tx | ||
.select() | ||
.from(FundingFeedback) | ||
.where(eq(FundingFeedback.fundingId, fundingId)); | ||
|
||
return result.map(row => MFundingComment.fromDBResult(row)); | ||
} | ||
.where(eq(FundingFeedback.fundingId, arg1)); | ||
|
||
async fetch(id: number): Promise<MFundingComment> { | ||
return this.db.transaction(async tx => this.fetchTx(tx, id)); | ||
return result.map(row => MFundingComment.from(row)); | ||
} | ||
|
||
async fetchTx(tx: DrizzleTransaction, id: number): Promise<MFundingComment> { | ||
const result = await tx | ||
.select() | ||
.from(FundingFeedback) | ||
.where(eq(FundingFeedback.id, id)); | ||
|
||
if (result.length === 0) { | ||
throw new Error(`Not found: FundingComment with id: ${id}`); | ||
async fetchAll(fundingId: number): Promise<MFundingComment[]>; | ||
async fetchAll(ids: number[]): Promise<MFundingComment[]>; | ||
async fetchAll(arg1: number | number[]): Promise<MFundingComment[]> { | ||
if (Array.isArray(arg1)) { | ||
return super.fetchAll(arg1); | ||
} | ||
return MFundingComment.fromDBResult(result[0]); | ||
} | ||
|
||
async insert(param: IFundingCommentRequestCreate): Promise<MFundingComment> { | ||
return this.db.transaction(async tx => this.insertTx(tx, param)); | ||
return this.withTransaction(async tx => this.fetchAllTx(tx, arg1)); | ||
} | ||
|
||
async insertTx( | ||
tx: DrizzleTransaction, | ||
param: IFundingCommentRequestCreate, | ||
param: IFundingCommentRequest, | ||
): Promise<MFundingComment> { | ||
const [comment] = await tx | ||
.insert(FundingFeedback) | ||
.values({ | ||
...param, | ||
fundingId: param.funding.id, | ||
chargedExecutiveId: param.chargedExecutive.id, | ||
feedback: param.content, | ||
createdAt: new Date(), | ||
}) | ||
.execute(); | ||
|
||
const newId = Number(comment.insertId); | ||
|
||
return this.fetchTx(tx, newId); | ||
const comment = { | ||
...param, | ||
fundingId: param.funding.id, | ||
chargedExecutiveId: param.chargedExecutive.id, | ||
feedback: param.content, | ||
}; | ||
return super.insertTx(tx, comment); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오버로딩도 잘 처리되나?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아주 잘되고, 예시 만들어둠