Skip to content

Commit

Permalink
Merge pull request #1419 from academic-relations/1399-fix-funding-api…
Browse files Browse the repository at this point in the history
…-for-executive-feedbacks

feat base repository and fix funding api for executive feedbacks
  • Loading branch information
pbc1017 authored Feb 5, 2025
2 parents 9f95207 + 0b8deac commit 78df9aa
Show file tree
Hide file tree
Showing 16 changed files with 417 additions and 297 deletions.
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));
}

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);
}

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";

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

0 comments on commit 78df9aa

Please sign in to comment.