Skip to content

Commit b481b40

Browse files
authored
Merge pull request #274 from solid-connection/release
[DEPLOY] v1.0.4
2 parents f8b2174 + 44f6cae commit b481b40

File tree

46 files changed

+923
-583
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+923
-583
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @Gyuhyeok99 @nayonsoso @wibaek

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI with Gradle
2+
3+
on:
4+
pull_request:
5+
branches: [ "develop", "release", "master" ]
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
checks: write
13+
14+
steps:
15+
- name: Checkout the code
16+
uses: actions/checkout@v4
17+
18+
- name: Set up JDK 17
19+
uses: actions/setup-java@v4
20+
with:
21+
java-version: '17'
22+
distribution: 'temurin'
23+
24+
- name: Setup Gradle
25+
uses: gradle/actions/setup-gradle@v4
26+
27+
- name: Make Gradle wrapper executable
28+
run: chmod +x ./gradlew
29+
30+
- name: Build with Gradle Wrapper
31+
run: ./gradlew build
32+
33+
- name: Publish Test Report
34+
uses: mikepenz/action-junit-report@v5
35+
if: success() || failure()
36+
with:
37+
report_paths: '**/build/test-results/test/TEST-*.xml'

src/main/java/com/example/solidconnection/application/controller/ApplicationController.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.example.solidconnection.application.service.ApplicationQueryService;
77
import com.example.solidconnection.application.service.ApplicationSubmissionService;
88
import com.example.solidconnection.custom.resolver.AuthorizedUser;
9+
import com.example.solidconnection.custom.security.annotation.RequireAdminAccess;
910
import com.example.solidconnection.siteuser.domain.SiteUser;
1011
import jakarta.validation.Valid;
1112
import lombok.RequiredArgsConstructor;
@@ -32,12 +33,13 @@ public ResponseEntity<ApplicationSubmissionResponse> apply(
3233
@AuthorizedUser SiteUser siteUser,
3334
@Valid @RequestBody ApplyRequest applyRequest
3435
) {
35-
boolean result = applicationSubmissionService.apply(siteUser, applyRequest);
36+
ApplicationSubmissionResponse applicationSubmissionResponse = applicationSubmissionService.apply(siteUser, applyRequest);
3637
return ResponseEntity
3738
.status(HttpStatus.OK)
38-
.body(new ApplicationSubmissionResponse(result));
39+
.body(applicationSubmissionResponse);
3940
}
4041

42+
@RequireAdminAccess
4143
@GetMapping
4244
public ResponseEntity<ApplicationsResponse> getApplicants(
4345
@AuthorizedUser SiteUser siteUser,

src/main/java/com/example/solidconnection/application/domain/Application.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class Application {
4646
@Column(length = 100)
4747
private String nicknameForApply;
4848

49-
@Column(columnDefinition = "int not null default 0")
49+
@Column(columnDefinition = "int not null default 1")
5050
private Integer updateCount;
5151

5252
@Column(length = 50, nullable = false)
@@ -76,7 +76,7 @@ public Application(
7676
this.gpa = gpa;
7777
this.languageTest = languageTest;
7878
this.term = term;
79-
this.updateCount = 0;
79+
this.updateCount = 1;
8080
this.verifyStatus = PENDING;
8181
}
8282

@@ -115,7 +115,7 @@ public Application(
115115
this.gpa = gpa;
116116
this.languageTest = languageTest;
117117
this.term = term;
118-
this.updateCount = 0;
118+
this.updateCount = 1;
119119
this.firstChoiceUniversity = firstChoiceUniversity;
120120
this.secondChoiceUniversity = secondChoiceUniversity;
121121
this.thirdChoiceUniversity = thirdChoiceUniversity;
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package com.example.solidconnection.application.dto;
22

3+
import com.example.solidconnection.application.domain.Application;
4+
35
public record ApplicationSubmissionResponse(
4-
boolean isSuccess) {
6+
int applyCount
7+
) {
8+
public static ApplicationSubmissionResponse from(Application application) {
9+
return new ApplicationSubmissionResponse(application.getUpdateCount());
10+
}
511
}

src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.example.solidconnection.application.service;
22

33
import com.example.solidconnection.application.domain.Application;
4+
import com.example.solidconnection.application.dto.ApplicationSubmissionResponse;
45
import com.example.solidconnection.application.dto.ApplyRequest;
56
import com.example.solidconnection.application.dto.UniversityChoiceRequest;
67
import com.example.solidconnection.application.repository.ApplicationRepository;
@@ -49,15 +50,10 @@ public class ApplicationSubmissionService {
4950
key = {"applications:all"},
5051
cacheManager = "customCacheManager"
5152
)
52-
public boolean apply(SiteUser siteUser, ApplyRequest applyRequest) {
53+
public ApplicationSubmissionResponse apply(SiteUser siteUser, ApplyRequest applyRequest) {
5354
UniversityChoiceRequest universityChoiceRequest = applyRequest.universityChoiceRequest();
54-
55-
Long gpaScoreId = applyRequest.gpaScoreId();
56-
Long languageTestScoreId = applyRequest.languageTestScoreId();
57-
GpaScore gpaScore = getValidGpaScore(siteUser, gpaScoreId);
58-
LanguageTestScore languageTestScore = getValidLanguageTestScore(siteUser, languageTestScoreId);
59-
60-
Optional<Application> application = applicationRepository.findBySiteUserAndTerm(siteUser, term);
55+
GpaScore gpaScore = getValidGpaScore(siteUser, applyRequest.gpaScoreId());
56+
LanguageTestScore languageTestScore = getValidLanguageTestScore(siteUser, applyRequest.languageTestScoreId());
6157

6258
UniversityInfoForApply firstChoiceUniversity = universityInfoForApplyRepository
6359
.getUniversityInfoForApplyByIdAndTerm(universityChoiceRequest.firstChoiceUniversityId(), term);
@@ -68,22 +64,19 @@ public boolean apply(SiteUser siteUser, ApplyRequest applyRequest) {
6864
.map(id -> universityInfoForApplyRepository.getUniversityInfoForApplyByIdAndTerm(id, term))
6965
.orElse(null);
7066

71-
if (application.isEmpty()) {
72-
Application newApplication = new Application(siteUser, gpaScore.getGpa(), languageTestScore.getLanguageTest(),
73-
term, firstChoiceUniversity, secondChoiceUniversity, thirdChoiceUniversity, getRandomNickname());
74-
newApplication.setVerifyStatus(VerifyStatus.APPROVED);
75-
applicationRepository.save(newApplication);
76-
} else {
77-
Application before = application.get();
78-
validateUpdateLimitNotExceed(before);
79-
before.setIsDeleteTrue(); // 기존 이력 soft delete 수행한다.
80-
81-
Application newApplication = new Application(siteUser, gpaScore.getGpa(), languageTestScore.getLanguageTest(),
82-
term, before.getUpdateCount() + 1, firstChoiceUniversity, secondChoiceUniversity, thirdChoiceUniversity, getRandomNickname());
83-
newApplication.setVerifyStatus(VerifyStatus.APPROVED);
84-
applicationRepository.save(newApplication);
85-
}
86-
return true;
67+
Optional<Application> existingApplication = applicationRepository.findBySiteUserAndTerm(siteUser, term);
68+
int updateCount = existingApplication
69+
.map(application -> {
70+
validateUpdateLimitNotExceed(application);
71+
application.setIsDeleteTrue();
72+
return application.getUpdateCount() + 1;
73+
})
74+
.orElse(1);
75+
Application newApplication = new Application(siteUser, gpaScore.getGpa(), languageTestScore.getLanguageTest(),
76+
term, updateCount, firstChoiceUniversity, secondChoiceUniversity, thirdChoiceUniversity, getRandomNickname());
77+
newApplication.setVerifyStatus(VerifyStatus.APPROVED);
78+
applicationRepository.save(newApplication);
79+
return ApplicationSubmissionResponse.from(newApplication);
8780
}
8881

8982
private GpaScore getValidGpaScore(SiteUser siteUser, Long gpaScoreId) {

src/main/java/com/example/solidconnection/auth/client/AppleOAuthClientSecretProvider.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,17 @@
99
import org.apache.tomcat.util.codec.binary.Base64;
1010
import org.springframework.stereotype.Component;
1111

12-
import java.io.BufferedReader;
13-
import java.io.InputStream;
14-
import java.io.InputStreamReader;
15-
import java.nio.charset.StandardCharsets;
1612
import java.security.KeyFactory;
13+
import java.security.NoSuchAlgorithmException;
1714
import java.security.PrivateKey;
15+
import java.security.spec.InvalidKeySpecException;
1816
import java.security.spec.PKCS8EncodedKeySpec;
1917
import java.util.Date;
20-
import java.util.stream.Collectors;
2118

2219
import static com.example.solidconnection.custom.exception.ErrorCode.FAILED_TO_READ_APPLE_PRIVATE_KEY;
2320

2421
/*
25-
* 애플 OAuth 에 필요하 클라이언트 시크릿은 매번 동적으로 생성해야 한다.
22+
* 애플 OAuth 에 필요한 클라이언트 시크릿은 매번 동적으로 생성해야 한다.
2623
* 클라이언트 시크릿은 애플 개발자 계정에서 발급받은 개인키(*.p8)를 사용하여 JWT 를 생성한다.
2724
* https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret
2825
* */
@@ -32,14 +29,13 @@ public class AppleOAuthClientSecretProvider {
3229

3330
private static final String KEY_ID_HEADER = "kid";
3431
private static final long TOKEN_DURATION = 1000 * 60 * 10; // 10min
35-
private static final String SECRET_KEY_PATH = "secret/AppleOAuthKey.p8";
3632

3733
private final AppleOAuthClientProperties appleOAuthClientProperties;
3834
private PrivateKey privateKey;
3935

4036
@PostConstruct
4137
private void initPrivateKey() {
42-
privateKey = readPrivateKey();
38+
privateKey = loadPrivateKey();
4339
}
4440

4541
public String generateClientSecret() {
@@ -57,16 +53,14 @@ public String generateClientSecret() {
5753
.compact();
5854
}
5955

60-
private PrivateKey readPrivateKey() {
61-
try (InputStream is = getClass().getClassLoader().getResourceAsStream(SECRET_KEY_PATH);
62-
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
63-
64-
String secretKey = reader.lines().collect(Collectors.joining("\n"));
56+
private PrivateKey loadPrivateKey() {
57+
try {
58+
String secretKey = appleOAuthClientProperties.secretKey();
6559
byte[] encoded = Base64.decodeBase64(secretKey);
6660
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
6761
KeyFactory keyFactory = KeyFactory.getInstance("EC");
6862
return keyFactory.generatePrivate(keySpec);
69-
} catch (Exception e) {
63+
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
7064
throw new CustomException(FAILED_TO_READ_APPLE_PRIVATE_KEY);
7165
}
7266
}

src/main/java/com/example/solidconnection/config/client/AppleOAuthClientProperties.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public record AppleOAuthClientProperties(
1010
String publicKeyUrl,
1111
String clientId,
1212
String teamId,
13-
String keyId
13+
String keyId,
14+
String secretKey
1415
) {
1516
}

src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import org.springframework.http.HttpStatus;
66

77
import static com.example.solidconnection.application.service.ApplicationSubmissionService.APPLICATION_UPDATE_COUNT_LIMIT;
8-
import static com.example.solidconnection.siteuser.service.SiteUserService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES;
8+
import static com.example.solidconnection.siteuser.service.MyPageService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES;
99

1010
@Getter
1111
@AllArgsConstructor
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.example.solidconnection.custom.security.annotation;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target(ElementType.METHOD)
9+
@Retention(RetentionPolicy.RUNTIME)
10+
public @interface RequireAdminAccess {
11+
}

0 commit comments

Comments
 (0)