Skip to content

Commit

Permalink
Merge pull request #66 from Nexters/main
Browse files Browse the repository at this point in the history
merge to master
  • Loading branch information
emost22 authored Jul 11, 2024
2 parents dbcd21d + 1b89c28 commit 0e65e38
Show file tree
Hide file tree
Showing 86 changed files with 3,347 additions and 243 deletions.
93 changes: 51 additions & 42 deletions .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: spurt CICD
on:
push:
branches:
- master
- main

jobs:
push_to_registry:
name: Push to ncp container registry
name: Push to aws container registry
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -19,14 +19,22 @@ jobs:
java-version: '17'
distribution: 'corretto'

- name: make application.yml
if: contains(github.ref, 'develop') || contains(github.ref, 'main')
run: |
cd ./src/main/resources
touch ./application.yml
echo "${{ secrets.YML_PROD }}" > ./application.yml
shell: bash

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: build -x spotlessJavaCheck
cache-read-only: ${{ github.ref != 'refs/heads/master' }}
arguments: build -x spotlessJavaCheck
cache-read-only: ${{ github.ref != 'refs/heads/master' }}

- name: save docs file
uses: actions/upload-artifact@v3
Expand All @@ -37,59 +45,60 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Login to NCP Container Registry
uses: docker/login-action@v2
with:
registry: ${{ secrets.NCP_CONTAINER_REGISTRY }}
username: ${{ secrets.NCP_ACCESS_KEY }}
password: ${{ secrets.NCP_SECRET_KEY }}
- name: Docker build & push to prod
if: contains(github.ref, 'develop') || contains(github.ref, 'main')
run: |
docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t spurt-be -f Dockerfile .
docker tag spurt-be:latest ${{ secrets.DOCKER_USER }}/spurt-be:latest
docker push ${{ secrets.DOCKER_USER }}/spurt-be:latest
- name: docker build and push
uses: docker/build-push-action@v3
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.NCP_CONTAINER_REGISTRY }}/spurt-backend:latest
cache-from: type=registry,ref=${{ secrets.NCP_CONTAINER_REGISTRY }}/spurt-backend:latest
cache-to: type=inline
secrets: |
GIT_AUTH_TOKEN=${{ secrets.GIT_TOKEN }}
pull_from_registry:
name: Connect server ssh and pull from container registry
needs: push_to_registry
runs-on: ubuntu-latest
steps:
- name: connect ssh
- name: Deploy to prod
if: contains(github.ref, 'develop') || contains(github.ref, 'main')
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.DEV_HOST }}
username: ${{ secrets.DEV_USERNAME }}
password: ${{ secrets.DEV_PASSWORD }}
port: ${{ secrets.DEV_PORT }}
host: ${{ secrets.HOST_NAME }}
username: ${{ secrets.AWS_USER_NAME }}
key: ${{ secrets.AWS_PRIVATE_KEY }}
port: ${{ secrets.AWS_PORT }}
script: |
docker login ${{ secrets.NCP_CONTAINER_REGISTRY }} -u ${{ secrets.NCP_ACCESS_KEY }} -p ${{ secrets.NCP_SECRET_KEY }}
docker pull ${{ secrets.NCP_CONTAINER_REGISTRY }}/spurt-backend:latest
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
docker run -d -p 8080:8080 ${{ secrets.NCP_CONTAINER_REGISTRY }}/spurt-backend:latest
docker image prune -f
docker pull ${{ secrets.DOCKER_USER }}/spurt-be:latest
docker stop spurt-be
docker rm spurt-be
docker run -d --network spurt --name spurt-be -p 8080:8080 ${{ secrets.DOCKER_USER }}/spurt-be
if docker images -f "dangling=true" -q | grep . > /dev/null; then
docker rmi $(docker images -f "dangling=true" -q)
fi
upload_docs:
name: upload api docs
needs: pull_from_registry
needs: push_to_registry
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }}
AWS_REGION: ap-northeast-2

steps:
- name: Download Artifact
uses: actions/download-artifact@v3
with:
name: api-docs # Artifact name
path: artifact
- name: Upload Files to S3
name: api-docs

- name: Upload binary to S3 bucket
uses: jakejarvis/s3-sync-action@master
env:
AWS_ACCESS_KEY_ID: ${{ secrets.NCP_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.NCP_SECRET_KEY }}
AWS_DEFAULT_REGION: us-east-1
run: |
aws --endpoint-url=https://kr.object.ncloudstorage.com s3 cp artifact/index.html s3://spurt-backend-docs
aws --endpoint-url=https://kr.object.ncloudstorage.com/ s3 ls s3://spurt-backend-docs --recursive | awk '{cmd="aws --endpoint-url=https://kr.object.ncloudstorage.com/ s3api put-object-acl --bucket spurt-backend-docs --acl public-read --key "$4; system(cmd)}'
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET_NAME }}
SOURCE_DIR: .

- name: Invalidate cache CloudFront
uses: chetan/invalidate-cloudfront-action@master
env:
DISTRIBUTION: ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }}
PATHS: '/*'
continue-on-error: true
37 changes: 37 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: spurt build check

on:
pull_request:
branches:
- main

jobs:
push_to_registry:
name: Push to aws container registry
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'corretto'

- name: make application.yml
if: github.event.pull_request.base.ref == 'main'
run: |
cd ./src/main/resources
touch ./application.yml
echo "${{ secrets.YML_PROD }}" > ./application.yml
shell: bash

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: build
cache-read-only: ${{ github.ref != 'refs/heads/master' }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ out/

### VS Code ###
.vscode/

application.yml
11 changes: 10 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ dependencies {
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

testImplementation 'com.h2database:h2:2.1.214'
compileOnly('com.h2database:h2:2.1.214')
}

// === Querydsl 빌드 옵션 ===
Expand All @@ -77,6 +79,12 @@ task jsonDoclet(type: Javadoc, dependsOn: compileTestJava) {

spotless {
java {
// generated 폴더는 제외
target project.fileTree(project.rootDir) {
include '**/*.java'
exclude '**/generated/**/*.*'
}

// 사용하지 않는 import 제거
removeUnusedImports()
// 구글 자바 포맷 적용
Expand All @@ -91,7 +99,8 @@ spotless {
}

// 의존성을 설정해줌 test 끝나고 spotlessjava 수행되도록
tasks.named("spotlessJava").configure { dependsOn("compileTestJava") }
// tasks.named("spotlessJava").configure { dependsOn("compileTestJava") }
tasks.named("spotlessJava").configure {dependsOn("asciidoctor")}

// 테스트 실행 후, snipperDir에 snippet(.adoc 파일들) 생성
test {
Expand Down
24 changes: 21 additions & 3 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ endif::[]
:toclevels: 2
:sectlinks:

넥스터즈 시리우스 팀 api 명세서
넥스터즈 게꽁지 팀 api 명세서

[[Spurt-API]]

Expand All @@ -21,7 +21,7 @@ endif::[]
|BASE URL

|DEV
|https://sirius-spurt.duckdns.org
|https://api.spurtapp.com
|===

== Header 정보
Expand All @@ -47,7 +47,7 @@ endif::[]
|정상 처리 되었습니다

|1000
|알 수 없는 애러가 발생했습니다.
|알 수 없는 에러가 발생했습니다.

|2000
|인증에 실패했습니다.
Expand All @@ -63,6 +63,22 @@ endif::[]

|4000
|시간 형식이 맞지 않습니다.

|5000
|ALL category는 저장할 수 없습니다.

|5001
|category는 최소 1개 이상 입력하셔야 합니다.

|6000
|조회 결과가 없습니다.

|7000
|질문은 3초에 1번만 작성이 가능합니다.

|7001
|경험은 3초에 1번만 작성이 가능합니다.

|===

== 현재 오류 사항
Expand All @@ -84,12 +100,14 @@ include::{snippets}/category-controller-test/카테고리_전체_조회_테스

== 직군 API
include::{snippets}/job-group-controller-test/유저_직군_저장/auto-section.adoc[]
include::{snippets}/job-group-controller-test/유저_직군_수정/auto-section.adoc[]

== 유저 API
include::{snippets}/user-controller-test/유저_존재_확인/auto-section.adoc[]
include::{snippets}/user-controller-test/유저_최초_질문_답변_작성_확인/auto-section.adoc[]
include::{snippets}/user-controller-test/유저_최초_핀고정_확인/auto-section.adoc[]
include::{snippets}/user-controller-test/유저_정보_조회/auto-section.adoc[]
include::{snippets}/user-controller-test/유저_삭제_확인/auto-section.adoc[]

== 프로젝트 API
include::{snippets}/experience-controller-test/본인_경험_저장/auto-section.adoc[]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.sirius.spurt.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
7 changes: 5 additions & 2 deletions src/main/java/com/sirius/spurt/common/meta/ResultCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

public enum ResultCode {
SUCCESS(0, "정상 처리 되었습니다"),
SYSTEM_ERROR(1000, "알 수 없는 애러가 발생했습니다."),
SYSTEM_ERROR(1000, "알 수 없는 에러가 발생했습니다."),
AUTHENTICATION_FAILED(2000, "인증에 실패했습니다."),
NOT_QUESTION_OWNER(3000, "질문이 존재하지 않거나 작성자가 아닙니다."),
NOT_EXIST_USER(3001, "존재하지 않는 유저입니다."),
NOT_EXPERIENCE_OWNER(3002, "경험이 존재하지 않거나 작성자가 아닙니다."),
TIME_FORMAT_ERROR(4000, "시간 형식이 맞지 않습니다."),
NOT_ALL_CATEGORY(5000, "ALL category는 저장할 수 없습니다."),
NO_CONTENT(6000, "조회 결과가 없습니다.");
MISSING_CATEGORY(5001, "category는 최소 1개 이상 입력하셔야 합니다."),
NO_CONTENT(6000, "조회 결과가 없습니다."),
QUESTION_THREE_SECONDS(7000, "질문은 3초에 1번만 작성이 가능합니다."),
EXPERIENCE_THREE_SECONDS(7001, "경험은 3초에 1번만 작성이 가능합니다.");

private Integer code;
private String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.sirius.spurt.common.resolver;

import com.sirius.spurt.common.exception.GlobalException;
import com.sirius.spurt.common.meta.JobGroup;
import com.sirius.spurt.common.meta.ResultCode;
import com.sirius.spurt.common.resolver.user.LoginUser;
import com.sirius.spurt.store.provider.auth.AuthProvider;
import com.sirius.spurt.store.provider.auth.vo.AuthVo;
import com.sirius.spurt.store.provider.jobgroup.JobGroupProvider;
import com.sirius.spurt.store.provider.user.UserProvider;
import com.sirius.spurt.store.provider.user.vo.UserVo;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
Expand Down Expand Up @@ -54,11 +52,11 @@ public Object resolveArgument(
String accessToken = accessHeader.replace(TOKEN_TYPE, "");
userInfo = authProvider.getUserId(accessToken);
// 로그인 이후 직군 미선택 유저 추가 체크
// UserVo userVo = userProvider.getUserInfo(userInfo.getUserId());
// if (userVo == null) {
// jobGroupProvider.saveJobGroup(
// userInfo.getUserId(), userInfo.getEmail(), JobGroup.DEVELOPER);
// }
// UserVo userVo = userProvider.getUserInfo(userInfo.getUserId());
// if (userVo == null) {
// jobGroupProvider.saveJobGroup(
// userInfo.getUserId(), userInfo.getEmail(), JobGroup.DEVELOPER);
// }
}
return new LoginUser(userInfo.getUserId(), userInfo.getEmail());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.sirius.spurt.common.validator;

import static com.sirius.spurt.common.meta.ResultCode.EXPERIENCE_THREE_SECONDS;
import static com.sirius.spurt.common.meta.ResultCode.NOT_EXPERIENCE_OWNER;
import static com.sirius.spurt.common.meta.ResultCode.NO_CONTENT;

import com.sirius.spurt.common.exception.GlobalException;
import com.sirius.spurt.store.repository.database.entity.ExperienceEntity;
import java.sql.Timestamp;
import java.util.List;
import org.springframework.util.CollectionUtils;

public class ExperienceValidator {
private static long EXPERIENCE_DUPLICATE_TIME = 3000L;

public static void validateNoContent(ExperienceEntity experienceEntity) {
if (!isExistExperience(experienceEntity)) {
throw new GlobalException(NO_CONTENT);
}
}

public static void validateNoContents(List<ExperienceEntity> experienceEntities) {
if (CollectionUtils.isEmpty(experienceEntities)) {
throw new GlobalException(NO_CONTENT);
}
}

public static void validate(ExperienceEntity experienceEntity) {
if (!isExistExperience(experienceEntity)) {
throw new GlobalException(NOT_EXPERIENCE_OWNER);
}
}

public static void validateTimestamp(ExperienceEntity experienceEntity) {
if (!isExistExperience(experienceEntity)) {
return;
}

if (isWithin3SecondsDifference(experienceEntity.getCreateTimestamp())) {
throw new GlobalException(EXPERIENCE_THREE_SECONDS);
}
}

private static boolean isExistExperience(ExperienceEntity experienceEntity) {
return experienceEntity != null;
}

private static boolean isWithin3SecondsDifference(Timestamp timestamp) {
return new Timestamp(System.currentTimeMillis()).getTime() - timestamp.getTime()
< EXPERIENCE_DUPLICATE_TIME;
}
}
Loading

0 comments on commit 0e65e38

Please sign in to comment.