Skip to content
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

Merged
merged 8 commits into from
Feb 5, 2025
3 changes: 3 additions & 0 deletions packages/api/src/common/model/entity.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export abstract class MEntity {
id: number;
}
140 changes: 140 additions & 0 deletions packages/api/src/common/repository/base.repository.ts
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));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오버로딩도 잘 처리되나?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아주 잘되고, 예시 만들어둠


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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

patch의 방향이 Partial 대신에 consumer 로 갔으면 좋겠음. 이렇게 했을 때 메리트는 단순히 필드 하나 바꾸는 동작 이상의 뭔가를 할수 있음. consumer 안에서 조건문을 쓰거나, 모델의 클래스 메서드를 호출하거나 등등 활용성이 아주 좋아짐.

Copy link
Contributor

Choose a reason for hiding this comment

The 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));
}
}
6 changes: 3 additions & 3 deletions packages/api/src/drizzle/schema/funding.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엥 이거 왜 바꼈죠


export const Funding = mysqlTable(
"funding",
Expand Down Expand Up @@ -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],
}),
}),
);
Expand Down Expand Up @@ -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],
}),
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,7 @@ export default class ActivityService {
await this.activityRepository.selectDurationByActivityId(row.id);
return {
...row,
durations: duration.sort((a, b) =>
a.startTerm.getTime() === b.startTerm.getTime()
? a.endTerm.getTime() - b.endTerm.getTime()
: a.startTerm.getTime() - b.startTerm.getTime(),
),
durations: duration,
};
}),
);
Expand Down
12 changes: 6 additions & 6 deletions packages/api/src/feature/funding/model/funding.comment.model.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { IFundingComment } from "@sparcs-clubs/interface/api/funding/type/funding.type";
import { IFundingComment } from "@sparcs-clubs/interface/api/funding/type/funding.comment.type";
import { FundingStatusEnum } from "@sparcs-clubs/interface/common/enum/funding.enum";
import { InferSelectModel } from "drizzle-orm";

import { MEntity } from "@sparcs-clubs/api/common/model/entity.model";
import { FundingFeedback } from "@sparcs-clubs/api/drizzle/schema/funding.schema";

import { MFunding } from "./funding.model";
import { VFundingSummary } from "./funding.summary.model";

export type FundingCommentDBResult = InferSelectModel<typeof FundingFeedback>;

export class MFundingComment implements IFundingComment {
id: number;
export type FundingCommentDbResult = InferSelectModel<typeof FundingFeedback>;

export class MFundingComment extends MEntity implements IFundingComment {
funding: { id: number };

chargedExecutive: {
Expand All @@ -27,6 +26,7 @@ export class MFundingComment implements IFundingComment {
createdAt: Date;

constructor(data: IFundingComment) {
super();
Object.assign(this, data);
}

Expand All @@ -38,7 +38,7 @@ export class MFundingComment implements IFundingComment {
);
}

static fromDBResult(result: FundingCommentDBResult) {
static from(result: FundingCommentDbResult): MFundingComment {
return new MFundingComment({
id: result.id,
funding: { id: result.fundingId },
Expand Down
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);
}
}
Loading