Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/mentor profile update #131

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/deploy-api-docs.yml
koreanddinghwan marked this conversation as resolved.
Show resolved Hide resolved
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