Skip to content

Commit

Permalink
Merge pull request #17 from Together42/7-rotation-refactoring
Browse files Browse the repository at this point in the history
Merge 7-rotation-refactoring to dev
  • Loading branch information
sideseal authored Nov 23, 2023
2 parents f0dec6c + eb65cba commit 3d021cb
Show file tree
Hide file tree
Showing 25 changed files with 11,930 additions and 707 deletions.
9,542 changes: 9,542 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"@nestjs/schedule": "^3.0.0",
"@nestjs/swagger": "^7.1.14",
"@nestjs/typeorm": "^10.0.0",
"@types/cron": "^2.4.0",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
Expand All @@ -41,11 +40,13 @@
"passport": "^0.6.0",
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1",
"typeorm-naming-strategies": "^4.1.0",
"@types/cron": "^2.4.0",
"axios": "^1.6.2",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"slack-block-builder": "^2.7.2",
"typeorm": "^0.3.17",
"typeorm-naming-strategies": "^4.1.0"
"typeorm": "^0.3.17"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
Expand Down
6 changes: 6 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { RotationsModule } from './rotations/rotations.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { HolidayModule } from './rotations/holiday.module';
import { MeetupsModule } from './meetups/meetups.module';
import { BatchModule } from './batch/batch.module';
import { SlackModule } from 'nestjs-slack';
Expand Down Expand Up @@ -40,6 +44,8 @@ import configuration from './config/configuration';
namingStrategy: new SnakeNamingStrategy(),
}),
}),
RotationsModule,
HolidayModule,
AuthModule,
UserModule,
MeetupsModule,
Expand Down
5 changes: 4 additions & 1 deletion src/config/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'dotenv/config';
import * as process from "process";
import * as process from 'process';

export default () => ({
database: {
Expand All @@ -22,4 +22,7 @@ export default () => ({
callbackUrl: process.env.GOOGLE_REDIRECT_URI || 'default',
prompt: process.env.GOOGLE_PROMPT || '',
},
openApi: {
serviceKey: process.env.SERVICE_KEY || '',
},
});
8 changes: 8 additions & 0 deletions src/rotations/dto/create-registration.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IsArray, IsDefined, IsNumber } from 'class-validator';

export class CreateRegistrationDto {
@IsDefined()
@IsArray()
@IsNumber({}, { each: true })
attendLimit: JSON;
}
16 changes: 16 additions & 0 deletions src/rotations/dto/create-rotation.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IsArray, IsDefined, IsNotEmpty, IsNumber } from 'class-validator';

export class CreateRotationDto {
@IsDefined()
@IsArray()
@IsNumber({}, { each: true })
attendDate: JSON;

@IsNotEmpty()
@IsNumber()
year: number;

@IsNotEmpty()
@IsNumber()
month: number;
}
4 changes: 4 additions & 0 deletions src/rotations/dto/update-registration.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateRegistrationDto } from './create-registration.dto';

export class UpdateRegistrationDto extends PartialType(CreateRegistrationDto) {}
29 changes: 29 additions & 0 deletions src/rotations/dto/update-rotation.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// import { PartialType } from '@nestjs/mapped-types';
// import { CreateRotationDto } from './create-rotation.dto';
import {
ArrayNotEmpty,
IsArray,
IsDefined,
IsNotEmpty,
IsNumber,
} from 'class-validator';

export class UpdateRotationDto {
@IsDefined()
@ArrayNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
attendDate: JSON;

@IsNotEmpty()
@IsNumber()
updateDate: number;

@IsNotEmpty()
@IsNumber()
year: number;

@IsNotEmpty()
@IsNumber()
month: number;
}
19 changes: 19 additions & 0 deletions src/rotations/entities/holiday/holiday.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity('rotation_holiday')
export class RotationHolidayEntity {
@PrimaryGeneratedColumn({ type: 'int', name: 'id' })
id: number;

@Column('smallint')
year: number;

@Column('tinyint')
month: number;

@Column('tinyint')
day: number;

@Column({ length: 50 })
info: string;
}
43 changes: 43 additions & 0 deletions src/rotations/entities/rotation/rotation-attendee.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
DeleteDateColumn,
ManyToOne,
JoinColumn,
} from 'typeorm';

@Entity('rotation_attendee')
export class RotationAttendeeEntity {
@PrimaryGeneratedColumn({ type: 'int', name: 'id' })
id: number;

@ManyToOne(() => User, (user) => user.id, {
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'user_id', referencedColumnName: 'id' }) // 설정 안하면 PK로 자동 매핑됨
user: User;

@Column('int')
userId: number;

@Column('smallint')
year: number;

@Column('tinyint')
month: number;

@Column('json')
attendLimit: JSON;

@CreateDateColumn({ type: 'datetime' })
createdAt: Date;

@UpdateDateColumn({ type: 'datetime' })
updatedAt: Date;

@DeleteDateColumn({ type: 'datetime' })
deletedAt: Date;
}
53 changes: 53 additions & 0 deletions src/rotations/entities/rotation/rotation.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
DeleteDateColumn,
ManyToOne,
JoinColumn,
} from 'typeorm';

@Entity('rotation')
export class RotationEntity {
@PrimaryGeneratedColumn({ type: 'int', name: 'id' })
id: number;

// eager: Rotation entity와 함께 User entity도 로드되도록 한다.
@ManyToOne(() => User, {
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'user_id', referencedColumnName: 'id' })
user: User;

@Column('int', { name: 'user_id' })
userId: number;

@ManyToOne(() => User, {
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'update_user_id', referencedColumnName: 'id' })
updateUser: User;

@Column('int', { name: 'update_user_id' })
updateUserId: number;

@Column('smallint')
year: number;

@Column('tinyint')
month: number;

@Column('tinyint')
day: number;

@CreateDateColumn({ type: 'datetime' })
createdAt: Date;

@UpdateDateColumn({ type: 'datetime' })
updatedAt: Date;

@DeleteDateColumn({ type: 'datetime' })
deletedAt: Date;
}
16 changes: 16 additions & 0 deletions src/rotations/holiday.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ScheduleModule } from '@nestjs/schedule';
import { RotationHolidayEntity } from './entities/holiday/holiday.entity';
import { HolidayService } from './holiday.service';
import { HolidayRepository } from './holiday.repository';

@Module({
imports: [
TypeOrmModule.forFeature([RotationHolidayEntity]),
ScheduleModule.forRoot(),
],
providers: [HolidayService, HolidayRepository],
exports: [HolidayService],
})
export class HolidayModule {}
35 changes: 35 additions & 0 deletions src/rotations/holiday.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { RotationHolidayEntity } from './entities/holiday/holiday.entity';

/* 레포지토리 연습용으로 한 번 만들어보기
* https://stackoverflow.com/questions/72549668/how-to-do-custom-repository-using-typeorm-mongodb-in-nestjs
*/
@Injectable()
export class HolidayRepository extends Repository<RotationHolidayEntity> {
constructor(private dataSource: DataSource) {
super(RotationHolidayEntity, dataSource.createEntityManager());
}

async findHolidayByYearAndMonth(
year: number,
month: number,
): Promise<number[]> {
try {
const records = await this.find({
where: {
year: year,
month: month,
},
});

if (records.length === 0) {
return [];
}

return records.map((record) => record.day);
} catch (error: any) {
throw new Error(`Error occurred in findHolidayByYearAndMonth: ${error}`);
}
}
}
82 changes: 82 additions & 0 deletions src/rotations/holiday.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Cron } from '@nestjs/schedule';
import { RotationHolidayEntity } from './entities/holiday/holiday.entity';
import { getHolidayArray } from './utils/holiday';
import { HolidayRepository } from './holiday.repository';

/* 인터페이스 파일을 따로 만들어야 하나? */
interface HolidayInfo {
year: number;
month: number;
day: number;
info: string;
}

@Injectable()
export class HolidayService {
private readonly logger = new Logger(HolidayService.name);

constructor(
@InjectRepository(RotationHolidayEntity)
private holidayRepository: Repository<RotationHolidayEntity>,
private myHolidayRepository: HolidayRepository,
) {}

/* 12월 25일 4시 42분에 실행 */
@Cron('42 4 25 12 *', {
name: 'saveHolidayInfo',
timeZone: 'Asia/Seoul',
})
async saveHolidayInfo(): Promise<void> {
try {
this.logger.log('Fetching holiday info...');

const holidayArray: HolidayInfo[] = await getHolidayArray();

for (const holidayInfo of holidayArray) {
const { year, month, day, info } = holidayInfo;

const recordExist = await this.holidayRepository.findOne({
where: {
year,
month,
day,
},
});

if (recordExist) {
continue;
}

const newHoliday = new RotationHolidayEntity();
newHoliday.year = year;
newHoliday.month = month;
newHoliday.day = day;
newHoliday.info = info;

await this.holidayRepository.save(newHoliday);
}
this.logger.log('Successfully stored holiday info!');
} catch (error: any) {
this.logger.error(error);
throw error;
}
}

async getHolidayByYearAndMonth(
year: number,
month: number,
): Promise<number[]> {
try {
return await this.myHolidayRepository.findHolidayByYearAndMonth(
year,
month,
);
} catch (error: any) {
this.logger.error(error);
throw error;
}
}
}
20 changes: 20 additions & 0 deletions src/rotations/rotations.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Test, TestingModule } from '@nestjs/testing';
import { RotationsController } from './rotations.controller';
import { RotationsService } from './rotations.service';

describe('RotationsController', () => {
let controller: RotationsController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [RotationsController],
providers: [RotationsService],
}).compile();

controller = module.get<RotationsController>(RotationsController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
Loading

0 comments on commit 3d021cb

Please sign in to comment.