Skip to content

Commit

Permalink
Refactor/mentor profile update (#131)
Browse files Browse the repository at this point in the history
* 🚀 [Docs, Deploy]: api docs trigger 생성

* ✨ [Feat]: schema 변경

- mentorProfile social link not null

* [Feat]: dto optional 제거

- 프론트측에서 optional dto를 사용하지 않는것으로 확인되어 optional
  해제했습니다.

* [Feat, Fix]: activation api 생성 ,null 체크 해제

- activation api를 새로 생성했습니다.

- dto에서 null check를하기때문에 controller에서 하지않습니다.

* [Feat]: dto update

- activation dto 별도로 생성.
- dto nullable 해제. socialLink는 optional로 null인경우 regex 확인X
- null이 아닌경우 regex에서 체크함.

* [Feat]: mentorProfile repository

- update할때 프로필 숨겨야할때 validation추가. socialLink의 경우,
  Null을 체크

* feat(mentorProfileService): update/activate/deactive 호출

* feat: update api-docs
  • Loading branch information
koreanddinghwan authored Oct 5, 2023
1 parent aa5e4aa commit 156b0da
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 71 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/deploy-api-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Trigger Api-Docs deployer

on:
push:
branches:
- develop
paths:
- 'api-docs.yml'

jobs:
trigger:
runs-on: ubuntu-latest
steps:
- name: api-docs repository의 dispatcher를 트리거합니다.
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.DISPATCHER_PAT_MYUKANG }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/manito42/api-docs/actions/workflows/71662751/dispatches \
-d '{"ref":"master"}'
55 changes: 49 additions & 6 deletions api-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,11 @@ paths:
summary: Update mentor profile
description: |
update mentor profiles.
**NOTE**: **hashtags** and **categories** are replaced with the new ones
**NOTE**: **hashtags** and **categories** are replaced with the new ones
**NOTE**: 프로필 활성화 조건에 위배되는 경우 isHide가 해제됩니다.
**사용법**: **description/shortDescription**
description이 **missing** **property** 인 경우, 무시(제외하고 업데이트)
description이 **""** 인 경우, ""로 업데이트됩니다.
description이 **null**인 경우 400 response를 리턴합니다.
tags:
- Mentor Profiles
security:
Expand Down Expand Up @@ -365,6 +365,43 @@ paths:
'409':
description: already exists

/mentor_profiles/{id}/activation:
patch:
summary: Activate or Deactivate mentorProfiles
description: |
mentorProfiled을 활성화/비활성화합니다.
tags:
- Mentor Profiles
security:
- OwnerUser: []
parameters:
- name: id
in: path
required: true
description: user id
schema:
type: integer
format: int32
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MentorProfileActivation'
responses:
'200':
description: Updated
content:
application/json:
schema:
$ref: '#/components/schemas/MentorProfileGet'
'400':
description: Invalid request parameter
'404':
description: Mentor profile not found



/reservations:
get:
summary: Get all reservations
Expand Down Expand Up @@ -1534,6 +1571,12 @@ components:
socialLink:
type: string

MentorProfileActivation:
type: object
properties:
isHide:
type: boolean

HomeGet:
type: object
properties:
Expand Down Expand Up @@ -1581,16 +1624,16 @@ components:
properties:
isHide:
type: boolean
description: 'optional, default false'
description: 'default false'
shortDescription:
type: string
description: 'optional, 0 <= len < 50'
description: '0 <= len < 50'
description:
type: string
description: 'optional, 0 <= len < 1000'
description: '0 <= len < 1000'
hashtags:
type: array
description: 'optional, number of hashtag <= 5'
description: 'number of hashtag <= 5'
items:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:
- Made the column `socialLink` on table `mentor_profiles` required. This step will fail if there are existing NULL values in that column.
*/
-- AlterTable
ALTER TABLE `mentor_profiles` MODIFY `socialLink` VARCHAR(255) NOT NULL;
2 changes: 1 addition & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ model MentorProfile {
categories Category[] @relation("profiles_categories")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0)
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
socialLink String? @db.VarChar(255)
socialLink String @db.VarChar(255)
@@map("mentor_profiles")
}
Expand Down
127 changes: 116 additions & 11 deletions src/database/repository/mentorProfile.repository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../services/prisma.service';
import { MentorProfileGetResponseDto } from '../../models/mentorProfile/dto/response/mentorProfileGetResponse.dto';
import { MentorProfileSelectQuery } from '../../models/mentorProfile/queries/mentorProfileSelect.query';
Expand All @@ -9,6 +9,7 @@ import {
import { MentorProfileUpdatePayloadDto } from '../../models/mentorProfile/dto/request/mentorProfileUpdatePayload.dto';
import { SelectAllType } from '../../common/constants/selectAll.type';
import { MentorProfilePaginationResponseDto } from 'src/models/mentorProfile/dto/response/mentorProfilePaginationResponse.dto';
import { Prisma } from '@prisma/client';

@Injectable()
export class MentorProfileRepository {
Expand Down Expand Up @@ -95,22 +96,126 @@ export class MentorProfileRepository {
});
}

/**
* @brief 멘토 프로필 업데이트
*
* @param number
* @param MentorProfileUpdatePayloadDto
*
* @description 멘토 프로필을 업데이트하는 함수입니다.
* - 멘토프로필업데이트시, 카테고리, 해시태그, 소셜링크가 없으면 isHide를 true로 업데이트합니다.
*/
async update(userId: number, data: MentorProfileUpdatePayloadDto) {
//validation for hashtags, categories, socialLink. 필수항목 사라지면 isHide를 true로.
return this.prisma.$transaction(async (prisma) => {
const profile = await prisma.mentorProfile.findUnique({
where: {
userId: userId,
},
select: {
hashtags: true,
categories: true,
socialLink: true,
isHide: true,
},
});

if (!profile) throw new NotFoundException('업데이트할 프로필이 없습니다.');

// 기존 프로필의 isHide 가져옴.
let isHide = profile.isHide;
// 업데이트할 카테고리가 0개인 경우
if (data.categories.length == 0) isHide = true;
// 업데이트할 해시태그가 0개인 경우
if (data.hashtags.length == 0) isHide = true;
// 업데이트할 소셜링크가 null인 경우
if (!data.socialLink) {
isHide = true;
data.socialLink = '';
}

return prisma.mentorProfile.update({
where: {
userId: userId,
},
data: {
shortDescription: data.shortDescription,
description: data.description,
hashtags: {
set: data.hashtags,
},
categories: {
set: data.categories,
},
isHide: isHide,
socialLink: data.socialLink,
},
select: MentorProfileSelectQuery,
});
});
}

/**
* @brief 멘토 프로필 활성화(isHide = false)
*
* @param userId
* @detail 멘토 프로필을 활성화 시키는 함수입니다.
* - 멘토 프로필 활성화위해선 카테고리가 최소 1개 이상 존재해야합니다.
* - 멘토 프로필 활성화위해선 해시태그가 최소 1개 이상 존재해야합니다.
* - 멘토 프로필 활성화위해선 소셜링크가 최소 1개 이상 존재해야합니다.
*/
async activateMentorProfile(userId: number) {
return this.prisma.$transaction(async (prisma) => {
const profile = await prisma.mentorProfile.findUnique({
where: {
userId: userId,
},
select: {
hashtags: true,
categories: true,
socialLink: true,
},
});

if (!profile) throw new NotFoundException('업데이트할 프로필이 없습니다.');

// 현재 프로필에 카테고리가 없는 경우
if (profile.categories.length === 0)
throw new BadRequestException('카테고리는 최소 1개 이상 선택해주세요.');

// 현재 프로필에 해시태그가 없는경우
if (profile.hashtags.length === 0)
throw new BadRequestException('해시태그는 최소 1개 이상 선택해주세요.');

// 현재 프로필에 소셜링크가 없는 경우
if (profile.socialLink.length === 0)
throw new BadRequestException('소셜 링크를 입력해주세요.');
return prisma.mentorProfile.update({
where: {
userId: userId,
},
data: {
isHide: false,
},
select: MentorProfileSelectQuery,
});
});
}

/**
* @brief 멘토 프로필 비활성화(isHide = true)
*
* @param number
* @description 멘토 프로필을 비활성화 시키는 함수입니다.
* - 멘토 프로필 비활성화시키면, 멘토 프로필이 검색되지 않습니다.
*/
async deActivateMentorProfile(userId: number) {
return this.prisma.mentorProfile.update({
where: {
userId: userId,
},
data: {
shortDescription: data.shortDescription,
description: data.description,
hashtags: {
set: data.hashtags,
},
categories: {
set: data.categories,
},
isHide: data.isHide,
socialLink: data.socialLink,
isHide: true,
},
select: MentorProfileSelectQuery,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IsBoolean } from 'class-validator';

export class MentorProfileActivateDto {
@IsBoolean({ message: 'isHide는 boolean 타입이어야 합니다' })
isHide: boolean;
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
import { ArrayMaxSize, IsOptional, IsString, Matches, MaxLength, MinLength } from 'class-validator';
import {
ArrayMaxSize,
IsArray,
IsOptional,
IsString,
Matches,
MaxLength,
MinLength,
} from 'class-validator';
import { IMentorProfileUpdateRequest } from '../../../../common/interfaces/api/mentorProfile/mentorProfileRequest.interface';

export class MentorProfileUpdatePayloadDto implements IMentorProfileUpdateRequest {
@MinLength(0, { message: 'shortDescription은 최소 0글자 이상이어야 합니다.' })
@MaxLength(50, {
message: 'shortDescription은 최대 50자 이하여야 합니다.',
})
@IsOptional()
shortDescription?: string;
shortDescription: string;

@MinLength(0, { message: 'description은 최소 0글자 이상이어야 합니다.' })
@MaxLength(1000, {
message: 'description은 최대 1000자 이하여야 합니다.',
})
@IsOptional()
description?: string;
description: string;

@ArrayMaxSize(5, { message: '해시태그는 5개 이하로 입력해주세요.' })
@IsOptional()
hashtags?: { id: number }[];

@IsOptional()
categories?: { id: number }[];
hashtags: { id: number }[];

@IsOptional()
isHide?: boolean;
@IsArray()
categories: { id: number }[];

@IsOptional()
@IsString()
@Matches('https://42born2code.slack.com/team/[a-zA-Z0-9_]+')
socialLink?: string;
@IsOptional()
@Matches('https://42born2code.slack.com/team/[a-zA-Z0-9_-]+')
socialLink: string;
}
34 changes: 17 additions & 17 deletions src/models/mentorProfile/mentorProfile.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { GetUserRole } from '../../common/decorators/getUserRole.decorator';
import { UserRole } from '@prisma/client';
import { GetUserId } from '../../common/decorators/getUserId.decorator';
import { MentorProfilePaginationResponseDto } from './dto/response/mentorProfilePaginationResponse.dto';
import { MentorProfileActivateDto } from './dto/request/mentorProfileActivate.dto';

@Controller('/mentor_profiles')
export class MentorProfileController {
Expand Down Expand Up @@ -62,23 +63,22 @@ export class MentorProfileController {
): Promise<MentorProfileGetResponseDto> {
if (id < 0) throw new BadRequestException();
if (role !== UserRole.ADMIN && tokenUserId !== id) throw new UnauthorizedException();
return await this.mentorProfileService.update(id, data);
}

if (data.description === null || data.shortDescription === null)
throw new BadRequestException('Description과 ShortDescription은 null이 될 수 없습니다');

if (data.isHide === false) {
if (!data.socialLink || data.socialLink == null)
throw new BadRequestException('멘토 프로필 활성화를 위해선 소셜 링크를 입력해야 합니다');
if (data.hashtags?.length === 0 || data.categories?.length === 0)
throw new BadRequestException(
'멘토 프로필 활성화를 위해선 해시태그와 카테고리를 최소 1개 이상 입력해야 합니다',
);
}

const updatedProfile = await this.mentorProfileService.update(id, data);

if (!updatedProfile) throw new NotFoundException();

return updatedProfile;
/**
* @access >= OWNER
*/
@Patch('/:id/activation')
@UseGuards(JwtGuard)
async activate(
@GetUserRole() role: UserRole,
@GetUserId() tokenUserId: number,
@Param('id') id: number,
@Body() data: MentorProfileActivateDto,
): Promise<MentorProfileGetResponseDto> {
if (id < 0) throw new BadRequestException();
if (role !== UserRole.ADMIN && tokenUserId !== id) throw new UnauthorizedException();
return await this.mentorProfileService.activateMentorProfile(id, data);
}
}
Loading

0 comments on commit 156b0da

Please sign in to comment.