-
Notifications
You must be signed in to change notification settings - Fork 0
feat: test api 재생성 & url 최대길이 증가 #98
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
Changes from all commits
6bb32ce
51e6732
4ba9de0
6b0be22
5d546a8
27c4795
fadfafd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,58 @@ | ||||||||||||||||||||||
| package com.pinback.api.test.controller; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import org.springframework.web.bind.annotation.DeleteMapping; | ||||||||||||||||||||||
| import org.springframework.web.bind.annotation.PathVariable; | ||||||||||||||||||||||
| import org.springframework.web.bind.annotation.PostMapping; | ||||||||||||||||||||||
| import org.springframework.web.bind.annotation.RequestBody; | ||||||||||||||||||||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||||||||||||||||||||
| import org.springframework.web.bind.annotation.RequestParam; | ||||||||||||||||||||||
| import org.springframework.web.bind.annotation.RestController; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import com.pinback.application.test.dto.request.PushTestRequest; | ||||||||||||||||||||||
| import com.pinback.application.test.dto.response.CategoriesTestResponse; | ||||||||||||||||||||||
| import com.pinback.application.test.port.in.TestPort; | ||||||||||||||||||||||
| import com.pinback.domain.user.entity.User; | ||||||||||||||||||||||
| import com.pinback.shared.annotation.CurrentUser; | ||||||||||||||||||||||
| import com.pinback.shared.dto.ResponseDto; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @RestController | ||||||||||||||||||||||
| @RequestMapping("/api/v1/test") | ||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||
| public class TestController { | ||||||||||||||||||||||
| private final TestPort testPort; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @PostMapping("/push") | ||||||||||||||||||||||
| public ResponseDto<Void> pushTest(@RequestBody PushTestRequest pushTestRequest) { | ||||||||||||||||||||||
| testPort.pushTest(pushTestRequest); | ||||||||||||||||||||||
| return ResponseDto.ok(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+27
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enable request validation on body - public ResponseDto<Void> pushTest(@RequestBody PushTestRequest pushTestRequest) {
+ public ResponseDto<Void> pushTest(@jakarta.validation.Valid @RequestBody PushTestRequest pushTestRequest) {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @PostMapping("/articles") | ||||||||||||||||||||||
| public ResponseDto<Void> createArticles( | ||||||||||||||||||||||
| @Parameter(hidden = true) @CurrentUser User user, | ||||||||||||||||||||||
| @RequestParam Long categoryId | ||||||||||||||||||||||
| ) { | ||||||||||||||||||||||
| testPort.createArticlesByCategory(user, categoryId); | ||||||||||||||||||||||
| return ResponseDto.ok(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @PostMapping("/categories") | ||||||||||||||||||||||
| public ResponseDto<CategoriesTestResponse> categoriesTest( | ||||||||||||||||||||||
| @Parameter(hidden = true) @CurrentUser User user | ||||||||||||||||||||||
| ) { | ||||||||||||||||||||||
| CategoriesTestResponse response = testPort.createCategories(user); | ||||||||||||||||||||||
| return ResponseDto.ok(response); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @DeleteMapping("/articles/{categoryId}") | ||||||||||||||||||||||
| public ResponseDto<Void> deleteTest( | ||||||||||||||||||||||
| @Parameter(hidden = true) @CurrentUser User user, | ||||||||||||||||||||||
| @PathVariable Long categoryId | ||||||||||||||||||||||
| ) { | ||||||||||||||||||||||
| testPort.deleteArticlesByCategory(user, categoryId); | ||||||||||||||||||||||
| return ResponseDto.ok(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.pinback.application.test.dto.request; | ||
|
|
||
| public record PushTestRequest( | ||
| String fcmToken, | ||
| String message | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.pinback.application.test.dto.response; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record CategoriesTestResponse( | ||
| List<String> categories | ||
| ) { | ||
| public static CategoriesTestResponse of(List<String> categories) { | ||
| return new CategoriesTestResponse(categories); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.pinback.application.test.port.in; | ||
|
|
||
| import com.pinback.application.test.dto.request.PushTestRequest; | ||
| import com.pinback.application.test.dto.response.CategoriesTestResponse; | ||
| import com.pinback.domain.user.entity.User; | ||
|
|
||
| public interface TestPort { | ||
| void pushTest(PushTestRequest request); | ||
|
|
||
| void createArticlesByCategory(User user, Long categoryId); | ||
|
|
||
| CategoriesTestResponse createCategories(User user); | ||
|
|
||
| void deleteArticlesByCategory(User user, Long categoryId); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package com.pinback.application.test.port.out; | ||
|
|
||
| public interface FcmServicePort { | ||
| void sendNotification(String token, String message); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| package com.pinback.application.test.usecase; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
| import java.util.UUID; | ||
|
|
||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import com.pinback.application.article.port.out.ArticleDeleteServicePort; | ||
| import com.pinback.application.article.port.out.ArticleSaveServicePort; | ||
| import com.pinback.application.category.port.out.CategoryColorServicePort; | ||
| import com.pinback.application.category.port.out.CategoryGetServicePort; | ||
| import com.pinback.application.category.port.out.CategorySaveServicePort; | ||
| import com.pinback.application.test.dto.request.PushTestRequest; | ||
| import com.pinback.application.test.dto.response.CategoriesTestResponse; | ||
| import com.pinback.application.test.port.in.TestPort; | ||
| import com.pinback.application.test.port.out.FcmServicePort; | ||
| import com.pinback.application.user.port.out.UserGetServicePort; | ||
| import com.pinback.domain.article.entity.Article; | ||
| import com.pinback.domain.category.entity.Category; | ||
| import com.pinback.domain.category.enums.CategoryColor; | ||
| import com.pinback.domain.user.entity.User; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class TestUsecase implements TestPort { | ||
| private static final int CATEGORY_LIMIT = 10; | ||
| private final FcmServicePort fcmService; | ||
| private final CategoryGetServicePort categoryGetServicePort; | ||
| private final ArticleSaveServicePort articleSaveServicePort; | ||
| private final UserGetServicePort userGetServicePort; | ||
| private final CategorySaveServicePort categorySaveServicePort; | ||
| private final CategoryColorServicePort categoryColorServicePort; | ||
| private final ArticleDeleteServicePort articleDeleteServicePort; | ||
|
|
||
| @Override | ||
| public void pushTest(PushTestRequest request) { | ||
| fcmService.sendNotification(request.fcmToken(), request.message()); | ||
| } | ||
|
|
||
| @Override | ||
| public void createArticlesByCategory(User user, Long categoryId) { | ||
| String format = "%s:%s"; | ||
| Category category = categoryGetServicePort.findById(categoryId); | ||
|
|
||
| for (int i = 0; i < 5; i++) { | ||
| Article article = Article.create( | ||
| String.format(format, user.getEmail(), UUID.randomUUID()), | ||
| "testMemo", | ||
| user, | ||
| category, | ||
| null | ||
| ); | ||
| articleSaveServicePort.save(article); | ||
| } | ||
| } | ||
|
|
||
|
Comment on lines
+45
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Wrap write operations in transactions Prevents partial writes on mid-loop failures. @Override
- public void createArticlesByCategory(User user, Long categoryId) {
+ @org.springframework.transaction.annotation.Transactional
+ public void createArticlesByCategory(User user, Long categoryId) {Also add (outside diff range) the import: import org.springframework.transaction.annotation.Transactional;I can annotate other write methods similarly in a follow-up diff below. 🤖 Prompt for AI Agents |
||
| @Override | ||
| public CategoriesTestResponse createCategories(User user) { | ||
| User getUser = userGetServicePort.findById(user.getId()); | ||
| List<String> defaultCategoryNames = Arrays.asList( | ||
| "집", | ||
| "취업", | ||
| "동아리", | ||
| "자기계발", | ||
| "포트폴리오", | ||
| "경제시사흐름", | ||
| "최신기술트렌드", | ||
| "인성직무면접꿀팁", | ||
| "어학자격증취득준비", | ||
| "멘탈관리스트레스해소" | ||
| ); | ||
|
|
||
| List<String> createdCategoryNames = new ArrayList<>(); | ||
| Set<CategoryColor> usedColors = categoryColorServicePort.getUsedColorsByUser(getUser); | ||
|
|
||
| for (int i = 0; i < 10; i++) { | ||
| String categoryName = defaultCategoryNames.get(i % defaultCategoryNames.size()); | ||
| if (i >= defaultCategoryNames.size()) { | ||
| categoryName = categoryName + "_" + (i - defaultCategoryNames.size() + 1); | ||
| } | ||
|
|
||
| CategoryColor availableColor = getNextAvailableColor(usedColors); | ||
| Category category = Category.create(categoryName, getUser, availableColor); | ||
| Category savedCategory = categorySaveServicePort.save(category); | ||
| createdCategoryNames.add(savedCategory.getName()); | ||
|
|
||
| // 사용된 색상 업데이트 | ||
| usedColors.add(availableColor); | ||
| } | ||
|
|
||
| return CategoriesTestResponse.of(createdCategoryNames); | ||
| } | ||
|
|
||
| private CategoryColor getNextAvailableColor(Set<CategoryColor> usedColors) { | ||
| return Arrays.stream(CategoryColor.values()) | ||
| .filter(color -> !usedColors.contains(color)) | ||
| .findFirst() | ||
| .orElse(CategoryColor.COLOR1); | ||
| } | ||
|
|
||
| @Override | ||
| public void deleteArticlesByCategory(User user, Long categoryId) { | ||
| Category category = categoryGetServicePort.findById(categoryId); | ||
| User getUser = userGetServicePort.findById(user.getId()); | ||
| articleDeleteServicePort.deleteByCategory(getUser, category.getId()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -155,7 +155,7 @@ public RemindArticlesWithCount findTodayRemindWithCount(UUID userId, Pageable pa | |||||||||
| .where(conditions) | ||||||||||
| .offset(pageable.getOffset()) | ||||||||||
| .limit(pageable.getPageSize()) | ||||||||||
| .orderBy(article.createdAt.desc()) | ||||||||||
| .orderBy(article.remindAt.asc()) | ||||||||||
| .fetch(); | ||||||||||
|
Comment on lines
+158
to
159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Switch to remindAt ASC is sensible, but add a deterministic tie-breaker for stable pagination. Without a secondary key, rows sharing the same remindAt can shuffle between pages. - .orderBy(article.remindAt.asc())
+ .orderBy(article.remindAt.asc(), article.id.asc())📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| JPAQuery<Long> countQuery = queryFactory | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.pinback.infrastructure.firebase; | ||
|
|
||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import com.pinback.application.test.port.out.FcmServicePort; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class FcmServiceAdapter implements FcmServicePort { | ||
| private final FcmService fcmService; | ||
|
|
||
| @Override | ||
| public void sendNotification(String token, String message) { | ||
| fcmService.sendNotification(token, message); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Gate “test” endpoints to non-prod (profile/feature flag or auth role)
These endpoints create/delete data and send push. Restrict to local/dev or admin.
If profiles aren’t feasible, add @PreAuthorize("hasRole('ADMIN')") or guard via a property (e.g., @ConditionalOnProperty("pinback.test.enabled", havingValue="true")).
📝 Committable suggestion
🤖 Prompt for AI Agents