Skip to content

Commit

Permalink
feat: ✨ daily activity detail records 추가
Browse files Browse the repository at this point in the history
- close #397
  • Loading branch information
jpham005 committed Nov 10, 2023
1 parent 4a871e6 commit cc2019d
Show file tree
Hide file tree
Showing 9 changed files with 494 additions and 21 deletions.
4 changes: 2 additions & 2 deletions app/src/cache/cache.util.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export class CacheUtilService {
.then((res) => res?.data);
}

async set(key: string, data: unknown): Promise<void> {
await this.cacheManager.set(key, data);
async set(key: string, data: unknown, ttl?: number): Promise<void> {
await this.cacheManager.set(key, data, ttl);
}

async setWithDate<T>(
Expand Down
40 changes: 37 additions & 3 deletions app/src/dailyActivity/dailyActivity.dto.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { DateRange } from 'src/dateRange/dtos/dateRange.dto';

export enum DailyActivityType {
CORRECTOR,
CORRECTED,
Expand All @@ -11,18 +13,50 @@ export type DailyLogtimeRecord = {
date: Date;
};

export type DailyLogtimeDetailRecord = Omit<DailyLogtimeRecord, 'date'>;

export type DailyDefaultRecord = {
type: Exclude<DailyActivityType, DailyActivityType.LOGTIME>;
id: number;
at: Date;
};

export type DailyEventDetailRecord = {
type: DailyActivityType.EVENT;
id: number;
name: string;
location: string;
beginAt: Date;
endAt: Date;
};

export type DailyEvaluationDetailRecord = {
type: DailyActivityType.CORRECTED | DailyActivityType.CORRECTOR;
id: number;
correctorLogin: string;
teamId: number;
leaderLogin: string;
projectName: string;
beginAt: Date;
filledAt: Date;
};

export type FindDailyActivityRecordInput = {
userId: number;
start: Date;
end: Date;
};
} & DateRange;

export type FindDailyActivityRecordOutput =
| DailyLogtimeRecord
| DailyDefaultRecord;

export type FindDailyActivityDetailRecordInput = {
userId: number;
idsWithType: {
type: Exclude<DailyActivityType, DailyActivityType.LOGTIME>;
id: number;
}[];
};

export type FindDailyActivityDetailRecordOutput =
| DailyEventDetailRecord
| DailyEvaluationDetailRecord;
126 changes: 126 additions & 0 deletions app/src/dailyActivity/dailyActivity.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { Test } from '@nestjs/testing';
import {
DailyActivityType,
type FindDailyActivityDetailRecordOutput,
} from './dailyActivity.dto';
import { DailyActivityService } from './dailyActivity.service';
import { DailyActivityDaoImpl } from './db/dailyActivity.database.dao';

describe(DailyActivityService.name, () => {
let dailyActivityService: DailyActivityService;

describe('userDailyActivityDetailRecordsById', () => {
const testUser = 1;
const testIdsWithType: {
id: number;
type: Exclude<DailyActivityType, DailyActivityType.LOGTIME>;
}[] = [
{
id: 2,
type: DailyActivityType.CORRECTOR,
},
{
id: 3,
type: DailyActivityType.CORRECTED,
},
{
id: 1,
type: DailyActivityType.EVENT,
},
];

beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
providers: [DailyActivityService],
})
.useMocker((token) => {
if (token === DailyActivityDaoImpl) {
return {
findAllRecordByDate: () => {},
findAllDetailRecordByDate:
(): FindDailyActivityDetailRecordOutput[] => {
return [
{
type: DailyActivityType.CORRECTOR,
// 정렬될 index를 id 로 사용
id: 2,
correctorLogin: 'corrector',
teamId: 1,
leaderLogin: 'leader1',
projectName: 'project',
beginAt: new Date('2021-01-01T20:00:00.000Z'),
filledAt: new Date('2021-01-01T21:00:00.000Z'),
},
{
type: DailyActivityType.CORRECTED,
id: 3,
correctorLogin: 'corrected',
teamId: 2,
leaderLogin: 'leader2',
projectName: 'project',
beginAt: new Date('2021-01-01T18:00:00.000Z'),
filledAt: new Date('2021-01-01T22:00:00.000Z'),
},
{
type: DailyActivityType.EVENT,
id: 1,
name: 'event',
location: 'location',
beginAt: new Date('2021-01-01T16:00:00.000Z'),
endAt: new Date('2021-01-01T17:00:00.000Z'),
},
];
},
};
}
})
.compile();

dailyActivityService = moduleRef.get(DailyActivityService);
});

it('DailyActivityDetailRecordUnion[] 을 반환함', async () => {
const result =
await dailyActivityService.userDailyActivityDetailRecordsById(
testUser,
testIdsWithType,
);

result.forEach((curr) => {
switch (curr.type) {
case DailyActivityType.EVENT:
expect(typeof curr.id === 'number').toBe(true);
expect(typeof curr.name === 'string').toBe(true);
expect(typeof curr.location === 'string').toBe(true);
expect(isNaN(curr.beginAt.getTime())).toBe(false);
expect(isNaN(curr.endAt.getTime())).toBe(false);
break;
case DailyActivityType.CORRECTOR:
case DailyActivityType.CORRECTED:
expect(typeof curr.id === 'number').toBe(true);
expect(typeof curr.correctorLogin === 'string').toBe(true);
expect(typeof curr.teamId === 'number').toBe(true);
expect(typeof curr.leaderLogin === 'string').toBe(true);
expect(typeof curr.projectName === 'string').toBe(true);
expect(isNaN(curr.beginAt.getTime())).toBe(false);
expect(isNaN(curr.filledAt.getTime())).toBe(false);
break;
default:
expect(1).toBe(2);
}
});
});

it('logtime 을 첫번째로, 나머지는 끝난 시간순으로 정렬하여 반환함', async () => {
const result =
await dailyActivityService.userDailyActivityDetailRecordsById(
testUser,
testIdsWithType,
);

result.forEach((curr, index) => {
expect(curr.id - 1).toBe(index);
});
});
});
});
50 changes: 46 additions & 4 deletions app/src/dailyActivity/dailyActivity.service.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { Injectable } from '@nestjs/common';
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import type { DateRange } from 'src/dateRange/dtos/dateRange.dto';
import { DateWrapper } from 'src/dateWrapper/dateWrapper';
import type { DailyActivity } from 'src/page/personal/general/models/personal.general.dailyActivity.model';
import { DailyActivityDaoImpl } from './db/dailyActivity.database.dao';
import type {
DailyActivity,
DailyActivityDetailRecordUnion,
} from 'src/page/personal/general/models/personal.general.dailyActivity.model';
import {
DailyActivityType,
type DailyDefaultRecord,
type DailyLogtimeDetailRecord,
type DailyLogtimeRecord,
type FindDailyActivityDetailRecordOutput,
} from './dailyActivity.dto';
import { DailyActivityDaoImpl } from './db/dailyActivity.database.dao';

@Injectable()
export class DailyActivityService {
constructor(private readonly dailyActivityDao: DailyActivityDaoImpl) {}

async findAllUserDailyActivityByDate(
async userDailyActivityByDate(
userId: number,
{ start, end }: DateRange,
): Promise<DailyActivity[]> {
Expand Down Expand Up @@ -55,8 +60,45 @@ export class DailyActivityService {
records,
}));
}

async userDailyActivityDetailRecordsById(
userId: number,
args: {
type: Exclude<DailyActivityType, DailyActivityType.LOGTIME>;
id: number;
}[],
): Promise<(typeof DailyActivityDetailRecordUnion)[]> {
const records = await this.dailyActivityDao.findAllDetailRecordByDate({
userId,
idsWithType: args,
});

return records.sort((a, b) => {
const aEndAt = getDetailRecordEndAt(a);
const bEndAt = getDetailRecordEndAt(b);

return aEndAt.getTime() - bEndAt.getTime();
});
}
}

const isDailyLogtimeRecord = (
record: DailyLogtimeRecord | DailyDefaultRecord,
): record is DailyLogtimeRecord => record.type === DailyActivityType.LOGTIME;

const getDetailRecordEndAt = (
dailyActivityDetailRecord: Exclude<
FindDailyActivityDetailRecordOutput,
DailyLogtimeDetailRecord
>,
): Date => {
switch (dailyActivityDetailRecord.type) {
case DailyActivityType.EVENT:
return dailyActivityDetailRecord.endAt;
case DailyActivityType.CORRECTED:
case DailyActivityType.CORRECTOR:
return dailyActivityDetailRecord.filledAt;
default:
throw new InternalServerErrorException('wrong getDetailRecordEndAt');
}
};
Loading

0 comments on commit cc2019d

Please sign in to comment.