diff --git a/src/app.module.ts b/src/app.module.ts index d6ab149..9e444ce 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,7 +6,6 @@ import { UserModule, FamilyMemberModule, FamilyScheduleModule, - PhotoModule, PostModule, InteractionModule, } from './module'; @@ -21,7 +20,6 @@ import { ConfigModule } from '@nestjs/config'; MysqlModule, FamilyMemberModule, FamilyScheduleModule, - PhotoModule, PostModule, InteractionModule, AuthModule, diff --git a/src/domain/family_member/dto/request/get-family-info.dto.ts b/src/domain/family_member/dto/request/get-family-info.dto.ts new file mode 100644 index 0000000..20d826b --- /dev/null +++ b/src/domain/family_member/dto/request/get-family-info.dto.ts @@ -0,0 +1,18 @@ +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class GetFamilyInfoDto { + @ApiProperty({ example: 1, description: '가족 고유 ID', nullable: false }) + @IsNotEmpty() + @IsNumber() + readonly familyMemberId: number; + + @ApiProperty({ + example: 'ausdifh.daifu0wierj', + description: '가족 멤버의 FCM 토큰', + nullable: false, + }) + @IsNotEmpty() + @IsString() + readonly fcmToken: string; +} diff --git a/src/domain/family_member/family-member.controller.ts b/src/domain/family_member/family-member.controller.ts index d13d444..6cb7645 100644 --- a/src/domain/family_member/family-member.controller.ts +++ b/src/domain/family_member/family-member.controller.ts @@ -22,6 +22,7 @@ import { CustomApiCreatedResponse } from '../../common/api/response-created.deco import { CustomApiOKResponse } from '../../common/api/response-ok.decorator'; import { ResponseFamilyDto } from '../family'; import { JwtServiceAuthGuard } from '../../auth/guards/jwt-service-auth.guard'; +import { GetFamilyInfoDto } from './dto/request/get-family-info.dto'; @ApiTags('가족 멤버 API') @Controller('api/family-member') @@ -133,16 +134,16 @@ export class FamilyMemberController { ); } - //가족 정보 반환 - @Get('/family') + //가족 정보 반환 (FCM 토큰까지 같이 보내기로 변경) + @Post('/family') @ApiOperation({ summary: '[가족 구성원] 가족 구성원이 속한 가족의 정보 조회', description: '가족 구성원이 속한 가족 정보를 반환한다.', }) - @CustomApiOKResponse(ResponseFamilyDto, '가족 정보를 반환한다.') - async findFamilyByMemberId(@Query('familyMemberId') familyMemberId: number) { + @CustomApiCreatedResponse(ResponseFamilyDto, '가족 정보를 반환한다.') + async findFamilyByMemberId(@Body() getFamilyInfoDto: GetFamilyInfoDto) { const responseFamilyDto: ResponseFamilyDto = - await this.familyMemberService.findFamilyByMemberId(familyMemberId); + await this.familyMemberService.findFamilyByMemberId(getFamilyInfoDto); return CustomApiResponse.success( ResponseCode.FAMILY_READ_SUCCESS, responseFamilyDto, diff --git a/src/domain/family_member/family-member.service.ts b/src/domain/family_member/family-member.service.ts index f1a4cd7..6108f91 100644 --- a/src/domain/family_member/family-member.service.ts +++ b/src/domain/family_member/family-member.service.ts @@ -12,6 +12,7 @@ import { UserException } from '../../common/exception/user.exception'; import { ResponseFamilyDto } from '../family'; import { FamilyMemberException } from '../../common/exception/family-member.exception'; import { FamilyException } from '../../common/exception/family.exception'; +import { GetFamilyInfoDto } from './dto/request/get-family-info.dto'; @Injectable() export class FamilyMemberService { @@ -111,16 +112,22 @@ export class FamilyMemberService { //가족 구성원 ID를 통한 가족 정보 반환 async findFamilyByMemberId( - familyMemberId: number, + getFamilyInfoDto: GetFamilyInfoDto, ): Promise { const familyMember = await this.familyMemberRepository.findOne({ - where: { id: familyMemberId }, + where: { id: getFamilyInfoDto.familyMemberId }, relations: ['family'], // Family 테이블과 Join }); if (!familyMember) { throw new FamilyMemberException(ResponseCode.FAMILY_MEMBER_NOT_FOUND); } + + // 가족 구성원의 FCM 토큰 업데이트 + await this.familyMemberRepository.update(familyMember.id, { + fcmToken: getFamilyInfoDto.fcmToken, + }); + return ResponseFamilyDto.from(familyMember.family); } diff --git a/src/domain/photo/dto/index.ts b/src/domain/photo/dto/index.ts deleted file mode 100644 index 84ff554..0000000 --- a/src/domain/photo/dto/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './request/create-photo.dto'; -export * from './request/update-photo.dto'; -export * from './response/response-photo.dto'; diff --git a/src/domain/photo/dto/request/create-photo.dto.ts b/src/domain/photo/dto/request/create-photo.dto.ts deleted file mode 100644 index 4f7cde8..0000000 --- a/src/domain/photo/dto/request/create-photo.dto.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { IsDate, IsNotEmpty, IsNumber, IsString } from 'class-validator'; -import { ApiProperty } from '@nestjs/swagger'; - -export class CreatePhotoDto { - @ApiProperty({ - example: 'https://s3.bucket.com/image1.jpg', - description: '사진의 S3 URL', - }) - @IsNotEmpty() - @IsString() - readonly s3imageUrl: string; - - @ApiProperty({ example: '푸앙이', description: '사진의 이름' }) - @IsNotEmpty() - @IsString() - readonly name: string; - - @ApiProperty({ example: '2021-10-10', description: '사진이 생성된 날짜' }) - @IsNotEmpty() - @IsDate() - readonly createdDate: Date; - - @ApiProperty({ example: 1, description: '사진을 소유한 가족의 고유 ID' }) - @IsNotEmpty() - @IsNumber() - readonly familyId: number; -} diff --git a/src/domain/photo/dto/request/update-photo.dto.ts b/src/domain/photo/dto/request/update-photo.dto.ts deleted file mode 100644 index 2c3b226..0000000 --- a/src/domain/photo/dto/request/update-photo.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreatePhotoDto } from './create-photo.dto'; -import { IsNotEmpty, IsNumber } from 'class-validator'; -import { ApiProperty } from '@nestjs/swagger'; - -export class UpdatePhotoDto extends PartialType(CreatePhotoDto) { - @ApiProperty({ example: 1, description: '사진의 고유 ID' }) - @IsNotEmpty() - @IsNumber() - photoId: number; -} diff --git a/src/domain/photo/dto/response/response-photo.dto.ts b/src/domain/photo/dto/response/response-photo.dto.ts deleted file mode 100644 index 6875f79..0000000 --- a/src/domain/photo/dto/response/response-photo.dto.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Photo } from '../../../../infra/entities'; -import { ApiProperty } from '@nestjs/swagger'; - -export class ResponsePhotoDto { - @ApiProperty({ example: 1, description: '사진 ID' }) - readonly photoId: number; - - @ApiProperty({ example: '푸앙이의 사진', description: '사진 이름' }) - readonly photoName: string; - - @ApiProperty({ example: 'http://s3.com/puang.jpg', description: '사진 URL' }) - readonly photoUrl: string; - - @ApiProperty({ example: '2021-01-01', description: '사진 생성 날짜' }) - readonly createdDate: Date; - - constructor( - photoId: number, - photoName: string, - photoUrl: string, - createdDate: Date, - ) { - this.photoId = photoId; - this.photoName = photoName; - this.photoUrl = photoUrl; - this.createdDate = createdDate; - } - static of( - photoId: number, - photoName: string, - photoUrl: string, - createdDate: Date, - ): ResponsePhotoDto { - return new ResponsePhotoDto(photoId, photoName, photoUrl, createdDate); - } - - static from(photo: Photo): ResponsePhotoDto { - return new ResponsePhotoDto( - photo.id, - photo.name, - photo.s3ImageUrl, - photo.createdDate, - ); - } -} diff --git a/src/domain/photo/index.ts b/src/domain/photo/index.ts deleted file mode 100644 index 24e4481..0000000 --- a/src/domain/photo/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './dto'; -export * from './photo.service'; -export * from './photo.controller'; diff --git a/src/domain/photo/photo.controller.ts b/src/domain/photo/photo.controller.ts deleted file mode 100644 index f5ce3cb..0000000 --- a/src/domain/photo/photo.controller.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { - Body, - Controller, - Delete, - Get, - Post, - Put, - Query, - UseGuards, -} from '@nestjs/common'; -import { PhotoService } from '../photo'; -import { CreatePhotoDto, ResponsePhotoDto, UpdatePhotoDto } from './dto'; -import { - ApiBearerAuth, - ApiOkResponse, - ApiOperation, - ApiTags, -} from '@nestjs/swagger'; -import { CustomApiResponse, ResponseCode } from '../../common'; -import { JwtServiceAuthGuard } from '../../auth/guards/jwt-service-auth.guard'; -import { CustomApiOKResponse } from '../../common/api/response-ok.decorator'; - -@ApiTags('사진 API') -@Controller('api/photo') -@UseGuards(JwtServiceAuthGuard) -@ApiBearerAuth('access-token') -export class PhotoController { - constructor(private readonly photoService: PhotoService) {} - - //사진 업로드 - @Post('') - @ApiOperation({ - summary: '[사진] 사진 업로드', - description: '사진을 업로드한다.', - }) - @CustomApiOKResponse( - Number, - '사진 업로드를 성공하면 Status Code 201과 photoId를 반환한다.', - ) - async createPhoto(@Body() createPhotoDto: CreatePhotoDto) { - const photoId = await this.photoService.createPhoto(createPhotoDto); - return CustomApiResponse.success( - ResponseCode.PHOTO_CREATED_SUCCESS, - photoId, - ); - } - - //사진 삭제 - @Delete('') - @ApiOperation({ - summary: '[사진] 사진 삭제', - description: '사진을 삭제한다.', - }) - @ApiOkResponse({ - description: '사진 삭제 성공할 시, Status Code 200를 반환한다', - }) - async deletePhoto(@Query('photoId') photoId: number) { - await this.photoService.deletePhoto(photoId); - return CustomApiResponse.success(ResponseCode.PHOTO_DELETE_SUCCESS, null); - } - - //사진 수정 - @Put('') - @ApiOperation({ - summary: '[사진] 사진 정보 수정', - description: '사진의 정보 수정한다.', - }) - @ApiOkResponse({ - description: '사진의 정보를 수정할 시, Status Code 200 반환', - }) - async updatePhoto(@Body() updatePhotoDto: UpdatePhotoDto) { - await this.photoService.updatePhotoInfo(updatePhotoDto); - return CustomApiResponse.success(ResponseCode.PHOTO_UPDATE_SUCCESS, null); - } - - //사진 리스트 반환 (pagination 적용) - @Get('/list') - @ApiOperation({ - summary: '[사진] 모든 가족 사진 반환', - description: - '가족의 모든 사진을 반환한다. 가족 Id와 페이지, 한 페이지당 사진 개수를 Query parameter로 받는다.', - }) - @CustomApiOKResponse(ResponsePhotoDto, 'ResponsePhotoDto의 배열을 반환한다.') - async getPhotos( - @Query('familyId') familyId: number, - @Query('page') page: number, - @Query('limit') limit: number, - ) { - const photos = await this.photoService.getPhotos(familyId, page, limit); - return CustomApiResponse.success(ResponseCode.PHOTO_READ_SUCCESS, photos); - } -} diff --git a/src/domain/photo/photo.service.ts b/src/domain/photo/photo.service.ts deleted file mode 100644 index 179f9aa..0000000 --- a/src/domain/photo/photo.service.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { CreatePhotoDto, ResponsePhotoDto, UpdatePhotoDto } from './dto'; -import { Family, Photo } from '../../infra/entities'; -import { Repository } from 'typeorm'; -import { InjectRepository } from '@nestjs/typeorm'; -import { FamilyException } from '../../common/exception/family.exception'; -import { ResponseCode } from '../../common'; -import { PhotoException } from '../../common/exception/photo.exception'; - -@Injectable() -export class PhotoService { - constructor( - @InjectRepository(Photo) private photoRepository: Repository, - @InjectRepository(Family) private familyRepository: Repository, - ) {} - - //사진 생성 (S3 Image URL 저장) - async createPhoto(createPhotoDto: CreatePhotoDto) { - const family: Family = await this.validateFamily(createPhotoDto.familyId); - const photo: Photo = Photo.createPhoto( - createPhotoDto.s3imageUrl, - createPhotoDto.name, - family, - ); - const savedPhoto: Photo = await this.photoRepository.save(photo); - return savedPhoto.id; - } - - //사진 삭제 (PhotoController에서 s3이미지 삭제한 후, DB에서 데이터 삭제)) - async deletePhoto(photoId: number) { - await this.validatePhoto(photoId); - await this.photoRepository.delete(photoId); - } - - //사진 페이지 조회 (Pagination) - async getPhotos(familyId: number, page: number, limit: number) { - await this.validateFamily(familyId); - const photos = await this.photoRepository.find({ - where: { id: familyId }, - }); - const startIndex = (page - 1) * limit; - const endIndex = page * limit; - return photos.slice(startIndex, endIndex).map((photo) => { - return ResponsePhotoDto.of( - photo.id, - photo.name, - photo.s3ImageUrl, - photo.createdDate, - ); - }); - } - - //사진 상세 조회 - async getPhotoInfo(photoId: number): Promise { - const photo: Photo = await this.validatePhoto(photoId); - return ResponsePhotoDto.of( - photo.id, - photo.name, - photo.s3ImageUrl, - photo.createdDate, - ); - } - - //사진 정보 수정 (이름) - async updatePhotoInfo(updatePhotoDto: UpdatePhotoDto) { - const photo: Photo = await this.validatePhoto(updatePhotoDto.photoId); - photo.name = updatePhotoDto.name; - await this.photoRepository.save(photo); - } - - async validatePhoto(photoId: number): Promise { - const photo: Photo = await this.photoRepository.findOne({ - where: { id: photoId }, - }); - if (!photo) { - throw new PhotoException(ResponseCode.PHOTO_NOT_FOUND); - } - return photo; - } - async validateFamily(familyId: number): Promise { - const family: Family = await this.familyRepository.findOne({ - where: { id: familyId }, - }); - if (!family) { - throw new FamilyException(ResponseCode.FAMILY_NOT_FOUND); - } - return family; - } -} diff --git a/src/domain/post/dto/request/create-post.dto.ts b/src/domain/post/dto/request/create-post.dto.ts index a1b224c..b772789 100644 --- a/src/domain/post/dto/request/create-post.dto.ts +++ b/src/domain/post/dto/request/create-post.dto.ts @@ -10,6 +10,11 @@ export class CreatePostDto { @IsNumber() readonly srcMemberId: number; + @ApiProperty({ example: 1, description: '포스트를 작성한 가족의 고유 ID' }) + @IsNotEmpty() + @IsNumber() + readonly familyId: number; + @ApiProperty({ example: '푸앙이', description: '포스트의 제목' }) @IsNotEmpty() @IsString() diff --git a/src/domain/post/dto/request/update-post.dto.ts b/src/domain/post/dto/request/update-post.dto.ts index 4394330..531c494 100644 --- a/src/domain/post/dto/request/update-post.dto.ts +++ b/src/domain/post/dto/request/update-post.dto.ts @@ -1,8 +1,10 @@ import { PartialType } from '@nestjs/mapped-types'; import { CreatePostDto } from './create-post.dto'; import { IsNotEmpty, IsNumber } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; export class UpdatePostDto extends PartialType(CreatePostDto) { + @ApiProperty({ example: 1, description: '포스트의 고유 ID' }) @IsNotEmpty() @IsNumber() postId: number; diff --git a/src/domain/post/dto/response/response-post.dto.ts b/src/domain/post/dto/response/response-post.dto.ts index 64b3bb8..c7725a3 100644 --- a/src/domain/post/dto/response/response-post.dto.ts +++ b/src/domain/post/dto/response/response-post.dto.ts @@ -20,28 +20,44 @@ export class ResponsePostDto { @ApiProperty({ example: '2021-10-10', description: '포스트가 작성된 날짜' }) readonly createdDate: Date; + @ApiProperty({ + example: 1, + description: '포스트를 작성한 가족 구성원의 가족 고유 ID', + }) + readonly familyId: number; + private constructor( postId: number, familyMemberId: number, title: string, context: string, createdDate: Date, + familyId: number, ) { this.postId = postId; this.familyMemberId = familyMemberId; this.title = title; this.context = context; this.createdDate = createdDate; + this.familyId = familyId; } static of( postId: number, - familyId: number, + familyMemberId: number, title: string, context: string, createdDate: Date, + familyId: number, ): ResponsePostDto { - return new ResponsePostDto(postId, familyId, title, context, createdDate); + return new ResponsePostDto( + postId, + familyMemberId, + title, + context, + createdDate, + familyId, + ); } static from(post: Post) { @@ -51,6 +67,7 @@ export class ResponsePostDto { post.title, post.context, post.createdDate, + post.family.id, ); } } diff --git a/src/domain/post/post.controller.ts b/src/domain/post/post.controller.ts index eca5aac..3e3acdf 100644 --- a/src/domain/post/post.controller.ts +++ b/src/domain/post/post.controller.ts @@ -75,16 +75,16 @@ export class PostController { //포스트 리스트 반환 @Get('') @ApiOperation({ - summary: '[게시글] 특정 구성원의 모든 게시글 반환', - description: '특정 구성원에게 등록된 모든 게시글들을 반환한다.', + summary: '[게시글] 특정 가족에 등록된 모든 게시글 반환', + description: '특정 가족에 등록된 모든 게시글들을 반환한다.', }) @CustomApiOKResponse( ResponsePostDto, '모든 게시글에 대한 정보를 배열 형태로 반환한다.', ) - async findPostList(@Query('familyMemberId') familyMemberId: number) { + async findPostList(@Query('familyId') familyId: number) { const responsePostDtoList: ResponsePostDto[] = - await this.postService.findPostListByMemberId(familyMemberId); + await this.postService.findPostListByFamilyId(familyId); return CustomApiResponse.success( ResponseCode.POST_READ_SUCCESS, responsePostDtoList, diff --git a/src/domain/post/post.service.ts b/src/domain/post/post.service.ts index 67fc8ca..e0c53c4 100644 --- a/src/domain/post/post.service.ts +++ b/src/domain/post/post.service.ts @@ -1,11 +1,12 @@ import { Injectable } from '@nestjs/common'; import { CreatePostDto, ResponsePostDto, UpdatePostDto } from './dto'; -import { FamilyMember, Post } from '../../infra/entities'; +import { Family, FamilyMember, Post } from '../../infra/entities'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { ResponseCode } from '../../common'; import { PostException } from '../../common/exception/post.exception'; import { FamilyMemberException } from '../../common/exception/family-member.exception'; +import { FamilyException } from '../../common/exception/family.exception'; @Injectable() export class PostService { @@ -13,17 +14,20 @@ export class PostService { @InjectRepository(FamilyMember) private familyMemberRepository: Repository, @InjectRepository(Post) private postRepository: Repository, + @InjectRepository(Family) private familyRepository: Repository, ) {} async createPost(createPostDto: CreatePostDto) { - const familyMember: FamilyMember = - await this.familyMemberRepository.findOne({ - where: { id: createPostDto.srcMemberId }, - }); + const familyMember = await this.validateFamilyMember( + createPostDto.srcMemberId, + ); + const family = await this.validateFamily(createPostDto.familyId); + const post: Post = Post.createPost( createPostDto.title, createPostDto.context, createPostDto.createdDate, familyMember, + family, ); const savedPost = await this.postRepository.save(post); return savedPost.id; @@ -38,13 +42,11 @@ export class PostService { await this.postRepository.save(post); } - async findPostListByMemberId( - familyMemberId: number, - ): Promise { - await this.validateFamilyMember(familyMemberId); + async findPostListByFamilyId(familyId: number): Promise { + await this.validateFamily(familyId); const postList = await this.postRepository.find({ - where: { srcMember: { id: familyMemberId } }, - relations: ['familyMember'], + where: { family: { id: familyId } }, + relations: ['family', 'familyMember'], }); return postList.map((post) => ResponsePostDto.from(post)); } @@ -73,4 +75,14 @@ export class PostService { } return familyMember; } + + async validateFamily(familyId: number) { + const family = await this.familyRepository.findOne({ + where: { id: familyId }, + }); + if (!family) { + throw new FamilyException(ResponseCode.FAMILY_NOT_FOUND); + } + return family; + } } diff --git a/src/domain/user/dto/request/update-user.dto.ts b/src/domain/user/dto/request/update-user.dto.ts index c4cad09..dfd37fb 100644 --- a/src/domain/user/dto/request/update-user.dto.ts +++ b/src/domain/user/dto/request/update-user.dto.ts @@ -1,9 +1,4 @@ import { PartialType } from '@nestjs/mapped-types'; import { CreateUserDto } from './create-user.dto'; -import { IsNotEmpty, IsNumber } from 'class-validator'; -export class UpdateUserDto extends PartialType(CreateUserDto) { - @IsNotEmpty() - @IsNumber() - userId: number; -} +export class UpdateUserDto extends PartialType(CreateUserDto) {} diff --git a/src/infra/entities/family-member.entity.ts b/src/infra/entities/family-member.entity.ts index 18b0f1a..4fdd665 100644 --- a/src/infra/entities/family-member.entity.ts +++ b/src/infra/entities/family-member.entity.ts @@ -48,8 +48,6 @@ export class FamilyMember { @OneToMany(() => Post, (post) => post.srcMember) sentPosts: Post[]; - @OneToMany(() => Post, (post) => post.dstMember) - gotPosts: Post[]; static createFamilyMember( role: number, family: Family, diff --git a/src/infra/entities/family.entity.ts b/src/infra/entities/family.entity.ts index 6617a00..b083845 100644 --- a/src/infra/entities/family.entity.ts +++ b/src/infra/entities/family.entity.ts @@ -1,7 +1,7 @@ import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; import { FamilyMember } from './family-member.entity'; import { FamilySchedule } from './family-schedule.entity'; -import { Photo } from './photo.entity'; +import { Post } from './post.entity'; @Entity('family', { schema: 'family_app_db' }) export class Family { @@ -26,8 +26,8 @@ export class Family { @OneToMany(() => FamilySchedule, (familySchedule) => familySchedule.family) familySchedules: FamilySchedule[]; - @OneToMany(() => Photo, (photo) => photo.family) - photos: Photo[]; + @OneToMany(() => Post, (post) => post.family) + posts: Post[]; static createFamily(familyName: string, keyCode: string): Family { const family = new Family(); diff --git a/src/infra/entities/index.ts b/src/infra/entities/index.ts index 1cb212d..2219fc4 100644 --- a/src/infra/entities/index.ts +++ b/src/infra/entities/index.ts @@ -1,7 +1,6 @@ export * from './family.entity'; export * from './family-member.entity'; export * from './family-schedule.entity'; -export * from './photo.entity'; export * from './post.entity'; export * from './user.entity'; export * from './interaction.entity'; diff --git a/src/infra/entities/photo.entity.ts b/src/infra/entities/photo.entity.ts deleted file mode 100644 index 3f6bb3d..0000000 --- a/src/infra/entities/photo.entity.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - PrimaryGeneratedColumn, -} from 'typeorm'; -import { Family } from './family.entity'; - -@Entity('photo', { schema: 'family_app_db' }) -export class Photo { - @PrimaryGeneratedColumn({ type: 'int', name: 'ID' }) - id: number; - - @Column('varchar', { name: 'S3_Image_URL', length: 100 }) - s3ImageUrl: string; - - @Column('varchar', { name: 'Name', length: 50 }) - name: string; - - @Column('date', { name: 'Created_Date' }) - createdDate: Date; - - @ManyToOne(() => Family, (family) => family.photos, { - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }) - @JoinColumn([{ name: 'family_ID', referencedColumnName: 'id' }]) - family: Family; - - static createPhoto(s3ImageUrl: string, name: string, family: Family): Photo { - const photo = new Photo(); - photo.s3ImageUrl = s3ImageUrl; - photo.name = name; - photo.createdDate = new Date(); - photo.family = family; - return photo; - } - - setId(id: number): void { - this.id = id; - } -} diff --git a/src/infra/entities/post.entity.ts b/src/infra/entities/post.entity.ts index 84b816e..6c41e01 100644 --- a/src/infra/entities/post.entity.ts +++ b/src/infra/entities/post.entity.ts @@ -6,6 +6,7 @@ import { PrimaryGeneratedColumn, } from 'typeorm'; import { FamilyMember } from './family-member.entity'; +import { Family } from './family.entity'; @Entity('post', { schema: 'family_app_db' }) export class Post { @@ -28,24 +29,26 @@ export class Post { @JoinColumn([{ name: 'Src_Member_ID', referencedColumnName: 'id' }]) srcMember: FamilyMember; - @ManyToOne(() => FamilyMember, (familyMember) => familyMember.gotPosts, { + @ManyToOne(() => Family, (family) => family.posts, { onDelete: 'CASCADE', onUpdate: 'CASCADE', }) - @JoinColumn([{ name: 'Dst_Member_ID', referencedColumnName: 'id' }]) - dstMember: FamilyMember; + @JoinColumn([{ name: 'Family_ID', referencedColumnName: 'id' }]) + family: Family; static createPost( title: string, context: string, createdDate: Date, srcMember: FamilyMember, + family: Family, ): Post { const post = new Post(); post.title = title; post.context = context; post.createdDate = createdDate; post.srcMember = srcMember; + post.family = family; return post; } } diff --git a/src/module/index.ts b/src/module/index.ts index 44fbf48..688535c 100644 --- a/src/module/index.ts +++ b/src/module/index.ts @@ -1,7 +1,6 @@ export * from './family-member.module'; export * from './family.module'; export * from './family-schedule.module'; -export * from './photo.module'; export * from './user.module'; export * from './post.module'; export * from './interaction.module'; diff --git a/src/module/photo.module.ts b/src/module/photo.module.ts deleted file mode 100644 index e04932c..0000000 --- a/src/module/photo.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { PhotoController, PhotoService } from '../domain/photo'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { Family, Photo } from '../infra/entities'; - -@Module({ - imports: [TypeOrmModule.forFeature([Family, Photo])], - - controllers: [PhotoController], - providers: [PhotoService], -}) -export class PhotoModule {} diff --git a/src/module/post.module.ts b/src/module/post.module.ts index 6dba0a2..196f53e 100644 --- a/src/module/post.module.ts +++ b/src/module/post.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { PostController, PostService } from '../domain/post'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { FamilyMember, Post } from '../infra/entities'; +import { Family, FamilyMember, Post } from '../infra/entities'; @Module({ - imports: [TypeOrmModule.forFeature([FamilyMember, Post])], + imports: [TypeOrmModule.forFeature([FamilyMember, Family, Post])], controllers: [PostController], providers: [PostService], }) diff --git a/src/test/e2e/family-member.e2e.spec.ts b/src/test/e2e/family-member.e2e.spec.ts index 9490475..eefc5e4 100644 --- a/src/test/e2e/family-member.e2e.spec.ts +++ b/src/test/e2e/family-member.e2e.spec.ts @@ -149,9 +149,12 @@ describe('FamilyMemberController (e2e)', () => { it('should get Family info with member id', async () => { const response = await request(app.getHttpServer()) - .get('/api/family-member/family') - .query({ id: 1 }) - .expect(200); + .post('/api/family-member/family') + .send({ + familyMemberId: 1, + fcmtoken: 'test', + }) + .expect(201); expect(response.body.message).toEqual('가족 조회 성공'); expect(response.body.data.familyName).toEqual('test'); diff --git a/src/test/e2e/photo.e2e.spec.ts b/src/test/e2e/photo.e2e.spec.ts deleted file mode 100644 index 133fe90..0000000 --- a/src/test/e2e/photo.e2e.spec.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import { Family, Photo } from '../../infra/entities'; -import { Repository } from 'typeorm'; -import { PhotoModule } from '../../module'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import * as request from 'supertest'; -import { PhotoService, ResponsePhotoDto } from '../../domain/photo'; -import { JwtServiceAuthGuard } from '../../auth/guards/jwt-service-auth.guard'; -import { MockJwtAuthGuard } from './mockAuthGuard'; -import { PassportModule } from '@nestjs/passport'; - -describe('PhotoController (e2e)', () => { - let app: INestApplication; - let mockPhotoService: Partial; - let mockPhotoRepository: Partial>; - let mockFamilyRepository: Partial>; - const photo: Photo = Photo.createPhoto( - 'test.com', - 'test', - Family.createFamily('test', 'testKeyCode'), - ); - photo.id = 1; - - beforeEach(async () => { - mockPhotoService = { - createPhoto: jest.fn().mockResolvedValue(1), - updatePhotoInfo: jest.fn(), - deletePhoto: jest.fn(), - getPhotos: jest.fn().mockResolvedValue([ResponsePhotoDto.from(photo)]), - getPhotoInfo: jest.fn().mockResolvedValue(ResponsePhotoDto.from(photo)), - }; - mockPhotoRepository = { - findOne: jest.fn(), - find: jest.fn(), - }; - mockFamilyRepository = { - findOne: jest.fn(), - find: jest.fn(), - }; - - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [ - PhotoModule, - PassportModule.register({ defaultStrategy: 'jwt-service' }), - ], - }) - .overrideProvider(PhotoService) - .useValue(mockPhotoService) - .overrideProvider(getRepositoryToken(Photo)) - .useValue(mockPhotoRepository) - .overrideProvider(getRepositoryToken(Family)) - .useValue(mockFamilyRepository) - .overrideGuard(JwtServiceAuthGuard) - .useClass(MockJwtAuthGuard) - .compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('should be defined', () => { - expect(app).toBeDefined(); - }); - - it('should create photo', async () => { - const response = await request(app.getHttpServer()) - .post('/api/photo') - .send({ - s3imageUrl: 'test', - name: 'test', - createdDate: new Date(), - familyId: 1, - }) - .expect(201); - - expect(response.body.message).toBe('사진 생성 성공'); - expect(response.body.data).toBe(1); - }); - - it('should update photo', async () => { - const response = await request(app.getHttpServer()) - .put('/api/photo') - .send({ - photoId: 1, - name: 'test', - }) - .expect(200); - - expect(response.body.message).toBe('사진 정보 수정 성공'); - }); - - it('should delete post', async () => { - const response = await request(app.getHttpServer()) - .delete('/api/photo') - .query({ photoId: 1 }) - .expect(200); - - expect(response.body.message).toBe('사진 삭제 성공'); - }); - - it('should get photos', async () => { - const response = await request(app.getHttpServer()) - .get('/api/photo/list') - .query({ familyId: 1, page: 1, limit: 10 }) - .expect(200); - - expect(response.body.message).toBe('사진 조회 성공'); - expect(response.body.data[0].photoId).toEqual(1); - expect(response.body.data[0].photoName).toEqual('test'); - expect(response.body.data[0].photoUrl).toEqual('test.com'); - }); -}); diff --git a/src/test/e2e/post.e2e.spec.ts b/src/test/e2e/post.e2e.spec.ts index 4ccc42a..681c740 100644 --- a/src/test/e2e/post.e2e.spec.ts +++ b/src/test/e2e/post.e2e.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { PostService, ResponsePostDto } from '../../domain/post'; import { INestApplication } from '@nestjs/common'; -import { FamilyMember, Post } from '../../infra/entities'; +import { Family, FamilyMember, Post } from '../../infra/entities'; import { Repository } from 'typeorm'; import { PostModule } from '../../module'; import { getRepositoryToken } from '@nestjs/typeorm'; @@ -14,12 +14,14 @@ describe('PostController (e2e)', () => { let app: INestApplication; let mockPostService: Partial; let mockPostRepository: Partial>; + let mockFamilyRepository: Partial>; let mockFamilyMemberRepository: Partial>; const post: Post = Post.createPost( 'testTitle', 'testContext', new Date(), FamilyMember.createFamilyMember(1, null, null, ''), + Family.createFamily('test', 'test'), ); beforeEach(async () => { @@ -27,7 +29,7 @@ describe('PostController (e2e)', () => { createPost: jest.fn().mockResolvedValue(1), updatePost: jest.fn(), deletePost: jest.fn(), - findPostListByMemberId: jest + findPostListByFamilyId: jest .fn() .mockResolvedValue([ResponsePostDto.from(post)]), }; @@ -40,6 +42,11 @@ describe('PostController (e2e)', () => { find: jest.fn(), }; + mockFamilyRepository = { + findOne: jest.fn(), + find: jest.fn(), + }; + const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [ PostModule, @@ -52,6 +59,8 @@ describe('PostController (e2e)', () => { .useValue(mockPostRepository) .overrideProvider(getRepositoryToken(FamilyMember)) .useValue(mockFamilyMemberRepository) + .overrideProvider(getRepositoryToken(Family)) + .useValue(mockFamilyRepository) .overrideGuard(JwtServiceAuthGuard) .useClass(MockJwtAuthGuard) .compile(); diff --git a/src/test/service/family-member.service.spec.ts b/src/test/service/family-member.service.spec.ts index 790be68..606cd94 100644 --- a/src/test/service/family-member.service.spec.ts +++ b/src/test/service/family-member.service.spec.ts @@ -6,6 +6,7 @@ import { } from '../../domain/family_member'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Family, FamilyMember, User } from '../../infra/entities'; +import { GetFamilyInfoDto } from '../../domain/family_member/dto/request/get-family-info.dto'; describe('FamilyMemberService', () => { const mockRepository = () => ({ @@ -134,6 +135,10 @@ describe('FamilyMemberService', () => { const family: Family = Family.createFamily('test', 'testKeyCode'); const user = User.createUser('test', 'test', 'test', 'test', 1, 1); const familyMember = FamilyMember.createFamilyMember(1, family, user, ''); + const getFamilyInfoDto: GetFamilyInfoDto = { + familyMemberId: 1, + fcmToken: 'test', + }; familyMember.setId(1); family.setId(1); @@ -142,7 +147,8 @@ describe('FamilyMemberService', () => { .mockResolvedValue(familyMember); jest.spyOn(familyRepository, 'findOne').mockResolvedValue(family); - const result = await familyMemberService.findFamilyByMemberId(1); + const result = + await familyMemberService.findFamilyByMemberId(getFamilyInfoDto); expect(result.familyId).toEqual(1); expect(result.familyName).toEqual('test'); }); diff --git a/src/test/service/interaction.service.spec.ts b/src/test/service/interaction.service.spec.ts index 3ffcfee..2e3a2bf 100644 --- a/src/test/service/interaction.service.spec.ts +++ b/src/test/service/interaction.service.spec.ts @@ -1,8 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { InteractionService } from '../../domain/interaction'; +import { InteractionService, CreateInteractionDto } from '../../domain/interaction'; import { Family, FamilyMember, Interaction, User } from '../../infra/entities'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { CreateInteractionDto } from '../../domain/interaction'; describe('InteractionService', () => { const mockRepository = () => ({ diff --git a/src/test/service/photo.service.spec.ts b/src/test/service/photo.service.spec.ts deleted file mode 100644 index fe4fbb0..0000000 --- a/src/test/service/photo.service.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { - CreatePhotoDto, - PhotoService, - ResponsePhotoDto, -} from '../../domain/photo'; -import { Family, Photo } from '../../infra/entities'; -import { getRepositoryToken } from '@nestjs/typeorm'; - -describe('PhotoService', () => { - const mockPhotoRepository = () => ({ - save: jest.fn(), - delete: jest.fn(), - find: jest.fn(), - findOne: jest.fn(), - }); - const mockFamilyRepository = () => ({ - findOne: jest.fn(), - }); - let photoService: PhotoService; - let photoRepository; - let familyRepository; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - PhotoService, - { - provide: getRepositoryToken(Photo), - useFactory: mockPhotoRepository, - }, - { - provide: getRepositoryToken(Family), - useFactory: mockFamilyRepository, - }, - ], - }).compile(); - - photoService = module.get(PhotoService); - photoRepository = module.get(getRepositoryToken(Photo)); - familyRepository = module.get(getRepositoryToken(Family)); - }); - - it('should be defined', () => { - expect(photoService).toBeDefined(); - expect(photoRepository).toBeDefined(); - expect(familyRepository).toBeDefined(); - }); - - it('should create photo', async () => { - const createPhotoDto: CreatePhotoDto = { - s3imageUrl: 'test', - name: 'test', - createdDate: new Date(), - familyId: 1, - }; - const family = Family.createFamily('test', 'testKeyCode'); - const photo = Photo.createPhoto( - createPhotoDto.s3imageUrl, - createPhotoDto.name, - family, - ); - photo.setId(1); - - jest.spyOn(familyRepository, 'findOne').mockResolvedValue(family); - jest.spyOn(photoRepository, 'save').mockResolvedValue(photo); - - const savedPhotoId = await photoService.createPhoto(createPhotoDto); - expect(savedPhotoId).toEqual(1); - }); - - it('should delete photo', async () => { - const photo = Photo.createPhoto( - 'test', - 'test', - Family.createFamily('test', 'testKeyCode'), - ); - photo.setId(1); - - jest.spyOn(photoRepository, 'findOne').mockResolvedValue(photo); - - await photoService.deletePhoto(1); - expect(photoRepository.delete).toBeCalledWith(1); - }); - - it('should get photos in pages', async () => { - const family = Family.createFamily('test', 'testKeyCode'); - const first_photo = Photo.createPhoto('testurl1', 'testone', family); - const second_photo = Photo.createPhoto('testurl2', 'testtwo', family); - - const photoList = [first_photo, second_photo]; - - first_photo.setId(2); - second_photo.setId(3); - family.setId(1); - - jest.spyOn(familyRepository, 'findOne').mockResolvedValue(family); - jest.spyOn(photoRepository, 'find').mockResolvedValue(photoList); - - const result: ResponsePhotoDto[] = await photoService.getPhotos( - family.id, - 1, - 1, - ); - - expect(result.length).toEqual(1); - expect(result[0].photoName).toEqual('testone'); - }); - - it('should get photo info', async () => { - const family = Family.createFamily('test', 'testKeyCode'); - const photo = Photo.createPhoto('testurl1', 'testone', family); - photo.setId(1); - - jest.spyOn(photoRepository, 'findOne').mockResolvedValue(photo); - - const result: ResponsePhotoDto = await photoService.getPhotoInfo(photo.id); - - expect(result.photoName).toEqual('testone'); - expect(result.photoUrl).toEqual('testurl1'); - }); - - it('should update photo info', async () => { - const family = Family.createFamily('test', 'testKeyCode'); - const oldPhoto = Photo.createPhoto('testurlold', 'testold', family); - - const updatePhotoDto = { - photoId: 1, - name: 'testnew', - }; - oldPhoto.setId(1); - - jest.spyOn(photoRepository, 'findOne').mockResolvedValue(oldPhoto); - jest.spyOn(photoRepository, 'save').mockResolvedValue(oldPhoto); - - await photoService.updatePhotoInfo(updatePhotoDto); - - expect(photoRepository.save).toBeCalledTimes(1); - }); -}); diff --git a/src/test/service/post.service.spec.ts b/src/test/service/post.service.spec.ts index 6e920fc..2fc6d42 100644 --- a/src/test/service/post.service.spec.ts +++ b/src/test/service/post.service.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CreatePostDto, PostService, UpdatePostDto } from '../../domain/post'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { FamilyMember, Post } from '../../infra/entities'; +import { Family, FamilyMember, Post } from '../../infra/entities'; describe('PostService', () => { const mockRepository = () => ({ @@ -14,6 +14,7 @@ describe('PostService', () => { let postService: PostService; let postRepository; let familyMemberRepository; + let familyRepository; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -27,12 +28,17 @@ describe('PostService', () => { provide: getRepositoryToken(FamilyMember), useFactory: mockRepository, }, + { + provide: getRepositoryToken(Family), + useFactory: mockRepository, + }, ], }).compile(); postService = module.get(PostService); postRepository = module.get(getRepositoryToken(Post)); familyMemberRepository = module.get(getRepositoryToken(FamilyMember)); + familyRepository = module.get(getRepositoryToken(Family)); }); it('should be defined', () => { @@ -40,11 +46,19 @@ describe('PostService', () => { }); it('should create post', async () => { + const family = Family.createFamily('test', 'test'); const familyMember = FamilyMember.createFamilyMember(1, null, null, ''); - const post = Post.createPost('test', 'test', new Date(), familyMember); + const post = Post.createPost( + 'test', + 'test', + new Date(), + familyMember, + family, + ); post.id = 1; const createPostDto: CreatePostDto = { + familyId: 1, srcMemberId: 1, title: 'test', context: 'test', @@ -55,6 +69,7 @@ describe('PostService', () => { .spyOn(familyMemberRepository, 'findOne') .mockResolvedValue(familyMember); jest.spyOn(postRepository, 'save').mockResolvedValue(post); + jest.spyOn(familyRepository, 'findOne').mockResolvedValue(family); const result = await postService.createPost(createPostDto); @@ -62,8 +77,15 @@ describe('PostService', () => { }); it('should update post', async () => { + const family = Family.createFamily('test', 'test'); const familyMember = FamilyMember.createFamilyMember(1, null, null, ''); - const post = Post.createPost('test', 'test', new Date(), familyMember); + const post = Post.createPost( + 'test', + 'test', + new Date(), + familyMember, + family, + ); post.id = 1; const updatePostDto: UpdatePostDto = { @@ -86,7 +108,13 @@ describe('PostService', () => { it('should delete post', async () => { const familyMember = FamilyMember.createFamilyMember(1, null, null, ''); - const post = Post.createPost('test', 'test', new Date(), familyMember); + const post = Post.createPost( + 'test', + 'test', + new Date(), + familyMember, + null, + ); post.id = 1; jest.spyOn(postRepository, 'findOne').mockResolvedValue(post); @@ -98,9 +126,16 @@ describe('PostService', () => { }); it('should get post list', async () => { + const family = Family.createFamily('test', 'test'); const familyMember = FamilyMember.createFamilyMember(1, null, null, ''); familyMember.id = 1; - const post = Post.createPost('test', 'test', new Date(), familyMember); + const post = Post.createPost( + 'test', + 'test', + new Date(), + familyMember, + family, + ); post.id = 1; const postList = [post]; @@ -109,8 +144,9 @@ describe('PostService', () => { jest .spyOn(familyMemberRepository, 'findOne') .mockResolvedValue(familyMember); + jest.spyOn(familyRepository, 'findOne').mockResolvedValue(family); - const result = await postService.findPostListByMemberId(1); + const result = await postService.findPostListByFamilyId(1); expect(result.length).toEqual(1); expect(result[0].title).toEqual('test'); diff --git a/src/test/service/user.service.spec.ts b/src/test/service/user.service.spec.ts index 8a64fb5..41cac2e 100644 --- a/src/test/service/user.service.spec.ts +++ b/src/test/service/user.service.spec.ts @@ -76,7 +76,6 @@ describe('UserService', () => { ); user.setId(1); const updateUserDto: UpdateUserDto = { - userId: 1, email: 'test@test.com', username: 'test', password: 'test',