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

[Merge] develop에 merge #38

Merged
merged 58 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
2b130b3
[#15] feat : 새 카테고리 추가
mmihye Jan 2, 2024
f7c807a
[#15] feat : 카테고리 삭제
mmihye Jan 2, 2024
dc7c067
[#15] feat : 전체 카테고리 조회
mmihye Jan 2, 2024
03548dd
[#15] feat : 카테고리 수정
mmihye Jan 3, 2024
8bd00e6
[#15] feat : 세부 카테고리 조회
mmihye Jan 3, 2024
ea3de1d
[#15] refactor : 필터에 따른 Dto매핑 코드 개선
mmihye Jan 4, 2024
d938c1f
[#15] refactor : delete 한번에 삭제하도록 수정
mmihye Jan 5, 2024
c402e03
[#15] refactor: 삭제전 연관관계 toast의 category null로 update
mmihye Jan 5, 2024
703cfdd
[#15] feat: category 우선순위 필드 추가 및 1 자동 증가
mmihye Jan 5, 2024
ec8e363
[#15] feat: category 조회시 우선순위로 정렬
mmihye Jan 5, 2024
25f1a52
[#15] feat: category 순서변경
mmihye Jan 5, 2024
3671720
[#15] refactor: 순서변경시 우선순위 증가인지 감소인지 구분
mmihye Jan 5, 2024
76e88ac
[#15] fix: category 삭제 후 우선순위 하나씩 당기기
mmihye Jan 5, 2024
11145d9
[#19] feature: s3 service, 썸네일 미리보기, 토스트 저장, 토스트 삭제 구현
sss4920 Jan 5, 2024
f189b83
Merge branch 'test' into feature/#15
mmihye Jan 6, 2024
18722d6
Merge pull request #17 from Link-MIND/feature/#15
mmihye Jan 6, 2024
6c33d90
[#23] fix: merge할때 발생한 오류 해결
mmihye Jan 6, 2024
3ac38a5
[#23] fix: remindEntity 수정
mmihye Jan 6, 2024
72cdc7b
[#23] feat: 타이머 생성
mmihye Jan 6, 2024
b7b10f8
[#23] feat: 타이머 조회 및 시간/날짜 수정
mmihye Jan 6, 2024
cd243db
[#23] feat: 타이머 삭제
mmihye Jan 6, 2024
4fd75bf
[#23] feat: 타이머 코멘트 수정
mmihye Jan 6, 2024
019c285
[#23] fix: 리마인드날짜 Integar으로 변경
mmihye Jan 6, 2024
78495da
[#23] feat: 타이머 페이지 조회
mmihye Jan 6, 2024
fa2168a
Merge branch 'test' into feature/#19
sss4920 Jan 7, 2024
ec1387b
Merge pull request #20 from Link-MIND/feature/#19
sss4920 Jan 7, 2024
48f244b
[#23] feat: 타이머 페이지 정렬
mmihye Jan 9, 2024
8551e83
[#23] refactor: 타이머 페이지 시간포멧 변경
mmihye Jan 9, 2024
44ce25f
[#23] refactor: 타이머 controller 메소드명 변경
mmihye Jan 9, 2024
1bd0c95
Merge branch 'test' into feature/#23
sss4920 Jan 9, 2024
d14ff27
Merge pull request #26 from Link-MIND/feature/#23
mmihye Jan 9, 2024
7659bf6
[#21] feature: 유저에 프로필이미지 설정, mypage, settings 페이지 구현
sss4920 Jan 9, 2024
5ab5d90
Merge branch 'test' into feature/#21
sss4920 Jan 9, 2024
181a797
[#27] feat: 푸시알림 구현
mmihye Jan 9, 2024
59bf11e
Merge pull request #28 from Link-MIND/feature/#21
sss4920 Jan 9, 2024
8c5a43d
[#29] refactor: 토스트 저장 로직 수정
sss4920 Jan 9, 2024
3c41483
[Refactor] 세부 카테고리 전체 가져오기 수정, 링크 Url 사진 주소 변경
sss4920 Jan 9, 2024
daa2509
[#29] refactor: 메인 페이지와 카테고리 페이지에서 검색기능 기획 단 서비스상 일치시킴
sss4920 Jan 9, 2024
039514e
[#29] refactor: 엑세스토큰, 리프레시 토큰 1일,3일로 변경, 모두 엑세스토큰을 이용해서 실행하도록 변경
sss4920 Jan 9, 2024
8417bd2
[#24] feature: 메인페이지 api 구현
mmihye Jan 9, 2024
d2fce78
[#24] feature: 메인페이지 api 엑세스 토큰으로 변경
mmihye Jan 9, 2024
e387eeb
[#24] feature: 카테고리 조회시 조회시간 업데이트
mmihye Jan 9, 2024
f0071a1
Merge branch 'test' into feature/#24
sss4920 Jan 9, 2024
3f1eb91
Merge pull request #32 from Link-MIND/feature/#24
sss4920 Jan 9, 2024
50b55db
Merge branch 'test' into refactor/#29
sss4920 Jan 9, 2024
d8a7e46
[#31] feature: slackApi 작성
sss4920 Jan 9, 2024
ed8a3d6
[#31] feature: sentry 설정 완료
sss4920 Jan 9, 2024
c1a4ae6
Merge pull request #30 from Link-MIND/refactor/#29
sss4920 Jan 9, 2024
8a7739d
[#35] fix: NPE 해결
sss4920 Jan 9, 2024
6171885
[#33] feature: 타이머 & 클립 에러처리
mmihye Jan 9, 2024
3855cd7
[#33] feature: 제목 글자 수 제한
mmihye Jan 9, 2024
e8013cb
Merge pull request #34 from Link-MIND/feature/#31
sss4920 Jan 9, 2024
022aac9
Merge pull request #36 from Link-MIND/Fix/#35
sss4920 Jan 9, 2024
61c330e
Merge branch 'test' into feature/#33
sss4920 Jan 9, 2024
09371b7
Merge pull request #37 from Link-MIND/feature/#33
sss4920 Jan 9, 2024
e71d8fd
fix : merge 에러 해결
mmihye Jan 9, 2024
4221deb
Merge remote-tracking branch 'origin/develop' into test
mmihye Jan 9, 2024
521ef7b
fix : sentry token 명 변경
mmihye Jan 9, 2024
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
3 changes: 3 additions & 0 deletions linkmind/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ application-local.yml
/src/test/resources/application-local.yml
/src/main/resources/application-dev.yml
/src/main/resources/application-local.yml
/src/main/resources/toaster-firebase-admin-sdk.json
/src/main/resources/toaster-firebase-admindk.json
/src/main/resources/toaster-firebase-admin-sdk.json
31 changes: 29 additions & 2 deletions linkmind/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
id "io.sentry.jvm.gradle" version "4.1.1"
}

group = 'com.app'
Expand Down Expand Up @@ -44,8 +45,8 @@ dependencies {
implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2'


//FCM
implementation group: 'com.google.firebase', name: 'firebase-admin', version: '6.8.1'
// FCM
implementation 'com.google.firebase:firebase-admin:9.1.1'
implementation 'com.squareup.okhttp3:okhttp:4.10.0' // Firebase 서버로 푸시 메시지 전송 시 필요

implementation 'org.springframework.boot:spring-boot-starter-actuator'
Expand All @@ -55,8 +56,34 @@ dependencies {
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

// S3 AWS
implementation("software.amazon.awssdk:bom:2.21.0")
implementation("software.amazon.awssdk:s3:2.21.0")
implementation 'org.apache.httpcomponents:httpclient:4.5.9'

// JSoup
implementation 'org.jsoup:jsoup:1.15.3'

// slack
implementation 'com.slack.api:slack-api-client:1.28.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.slack.api:slack-app-backend:1.28.0'
implementation 'com.slack.api:slack-api-model:1.28.0'

}

tasks.named('test') {
useJUnitPlatform()
}

sentry {
// Generates a JVM (Java, Kotlin, etc.) source bundle and uploads your source code to Sentry.
// This enables source context, allowing you to see your source
// code as part of your stack traces in Sentry.
includeSourceContext = true

org = "linkmind"
projectName = "java-spring-boot"
authToken = System.getenv("SENTRY_TOKEN")
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.app.toaster.common.advice;

import java.io.IOException;
import java.net.MalformedURLException;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
Expand All @@ -13,21 +18,28 @@
import com.app.toaster.common.dto.ApiResponse;
import com.app.toaster.exception.Error;
import com.app.toaster.exception.model.CustomException;
import com.app.toaster.external.client.slack.SlackApi;

import io.sentry.Sentry;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintDefinitionException;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@RestControllerAdvice
@Component
@NoArgsConstructor
@RequiredArgsConstructor
public class ControllerExceptionAdvice {
private final SlackApi slackApi;

/**
* custom error
*/
@ExceptionHandler(CustomException.class)
protected ResponseEntity<ApiResponse> handleCustomException(CustomException e) {
Sentry.captureException(e);
return ResponseEntity.status(e.getHttpStatus())
.body(ApiResponse.error(e.getError(), e.getMessage()));
.body(ApiResponse.error(e.getError(), e.getMessage()));
}

// @ExceptionHandler(IllegalArgumentException.class)
Expand All @@ -38,7 +50,33 @@ protected ResponseEntity<ApiResponse> handleCustomException(CustomException e) {
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ApiResponse> handleConstraintDefinitionException(final MethodArgumentNotValidException e) {
FieldError fieldError = e.getBindingResult().getFieldError();
Sentry.captureException(e);
return ResponseEntity.status(e.getStatusCode())
.body(ApiResponse.error(Error.BAD_REQUEST_VALIDATION, fieldError.getDefaultMessage()));
.body(ApiResponse.error(Error.BAD_REQUEST_VALIDATION, fieldError.getDefaultMessage()));
}

@ExceptionHandler(MalformedURLException.class)
protected ApiResponse handleConstraintDefinitionException(final MalformedURLException e) {
Sentry.captureException(e);
return ApiResponse.error(Error.MALFORMED_URL_EXEPTION, Error.MALFORMED_URL_EXEPTION.getMessage());
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(DateTimeParseException.class)
protected ApiResponse handleDateTimeParseException(final DateTimeParseException e) {
return ApiResponse.error(Error.BAD_REQUEST_REMIND_TIME, Error.BAD_REQUEST_REMIND_TIME.getMessage());
}

/**
* 500 Internal Server Error
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
protected ApiResponse<Object> handleException(final Exception error, final HttpServletRequest request) throws
IOException {
slackApi.sendAlert(error, request);
Sentry.captureException(error);
return ApiResponse.error(Error.INTERNAL_SERVER_ERROR);
}

}
40 changes: 40 additions & 0 deletions linkmind/src/main/java/com/app/toaster/config/fcm/FCMConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.app.toaster.config.fcm;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.*;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;

@Slf4j
@Configuration
public class FCMConfig {

@Value("${fcm.key.path}")
private String SERViCE_ACCOUNT_JSON;

@PostConstruct
public void init() {
try {
ClassPathResource resource = new ClassPathResource(SERViCE_ACCOUNT_JSON);
InputStream serviceAccount = resource.getInputStream();

FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();

FirebaseApp.initializeApp(options);
log.info("파이어베이스 서버와의 연결에 성공했습니다.");
} catch (IOException e) {
log.error("파이어베이스 서버와의 연결에 실패했습니다.");
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.app.toaster.controller;

import java.io.IOException;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
Expand Down Expand Up @@ -31,7 +33,7 @@ public class AuthController {
public ApiResponse<SignInResponseDto> signIn(
@RequestHeader("Authorization") String socialAccessToken,
@RequestBody SignInRequestDto requestDto
) {
) throws IOException {
return ApiResponse.success(Success.LOGIN_SUCCESS, authService.signIn(socialAccessToken, requestDto));
}

Expand All @@ -44,7 +46,6 @@ public ApiResponse<TokenResponseDto> reissueToken(@RequestHeader String refreshT
@PostMapping("/sign-out")
@ResponseStatus(HttpStatus.OK)
public ApiResponse signOut(@UserId Long userId) {
System.out.println(userId);
authService.signOut(userId);
return ApiResponse.success(Success.SIGNOUT_SUCCESS);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,82 @@
package com.app.toaster.controller;

import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.app.toaster.common.dto.ApiResponse;
import com.app.toaster.domain.Category;
import com.app.toaster.infrastructure.CategoryRepository;
import com.app.toaster.config.UserId;
import com.app.toaster.controller.request.category.*;
import com.app.toaster.controller.response.toast.ToastFilter;
import com.app.toaster.controller.response.category.CategoriesReponse;
import com.app.toaster.controller.response.category.GetCategoryResponseDto;
import com.app.toaster.exception.Success;
import com.app.toaster.service.category.CategoryService;
import com.app.toaster.service.search.SearchService;

import jakarta.validation.Valid;
import jakarta.websocket.server.PathParam;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/category")
@RequiredArgsConstructor
@Validated
public class CategoryController {
private final SearchService searchService;

@GetMapping("/search")
public ApiResponse searchProducts(@RequestHeader Long userId ,@RequestParam("query") String query){
return searchService.searchCategoryTitle(userId,query);
}
private final CategoryService categoryService;
private final SearchService searchService;

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ApiResponse createCateory(
@UserId Long userId,
@Valid @RequestBody CreateCategoryDto createCategoryDto
){
categoryService.createCategory(userId, createCategoryDto);
return ApiResponse.success(Success.CREATE_CATEGORY_SUCCESS);
}

@DeleteMapping
@ResponseStatus(HttpStatus.OK)
public ApiResponse deleteCategory(
@UserId Long userId,
@RequestBody DeleteCategoryDto deleteCategoryDto
){
categoryService.deleteCategory(userId, deleteCategoryDto);
return ApiResponse.success(Success.DELETE_CATEGORY_SUCCESS);
}

@GetMapping("/all")
@ResponseStatus(HttpStatus.OK)
public ApiResponse<List<CategoriesReponse>> getCategories(@UserId Long userId){
return ApiResponse.success(Success.GET_CATEORIES_SUCCESS, categoryService.getCategories(userId));
}

@PatchMapping("/edit")
@ResponseStatus(HttpStatus.OK)
public ApiResponse editCategories(
@UserId Long userId,
@RequestBody EditCategoryRequestDto editCategoryRequestDto
){
categoryService.editCategories(userId, editCategoryRequestDto);
return ApiResponse.success(Success.UPDATE_CATEGORY_TITLE_SUCCESS);
}

@GetMapping("/{categoryId}")
@ResponseStatus(HttpStatus.OK)
public ApiResponse<GetCategoryResponseDto> getCategory(
@UserId Long userId,
@PathVariable Long categoryId,
@RequestParam("filter") ToastFilter filter
){
return ApiResponse.success(Success.GET_CATEORY_SUCCESS,categoryService.getCategory(userId, categoryId, filter));
}


@GetMapping("/search")
public ApiResponse searchProducts(@UserId Long userId ,@RequestParam("query") String query){
return searchService.searchMain(userId,query);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.app.toaster.controller;

import com.app.toaster.controller.request.fcm.FCMPushRequestDto;
import com.app.toaster.service.fcm.FCMService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;

@RestController
@RequestMapping("/alarm")
@RequiredArgsConstructor
public class FCMController {

private final FCMService fcmService;

/**
* 헤더와 바디를 직접 만들어 알림을 전송하는 테스트용 API (상대 답변 알람 전송에 사용)
*/
@PostMapping
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<String> sendNotificationByToken(@RequestBody FCMPushRequestDto request) throws IOException {

fcmService.pushAlarm(request);
return ResponseEntity.ok().body("푸시알림 전송에 성공했습니다!");
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.app.toaster.controller;

import com.app.toaster.config.UserId;
import com.app.toaster.controller.response.main.MainPageResponseDto;
import com.app.toaster.controller.response.timer.GetTimerResponseDto;
import com.app.toaster.exception.Success;
import com.app.toaster.service.main.MainService;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import com.app.toaster.common.dto.ApiResponse;
import com.app.toaster.config.UserId;
import com.app.toaster.service.UserService;
import com.app.toaster.service.search.SearchService;
import com.app.toaster.service.toast.ToastService;

Expand All @@ -18,9 +22,20 @@
@RequestMapping("/main")
public class MainController {
private final SearchService searchService;
private final MainService mainService;

private final UserService userService;

@GetMapping("/search")
public ApiResponse searchProducts(@RequestHeader Long userId ,@RequestParam("query") String query){
public ApiResponse searchProducts(@UserId Long userId ,@RequestParam("query") String query){
return searchService.searchMain(userId,query);
}

@GetMapping
@ResponseStatus(HttpStatus.OK)
public ApiResponse<MainPageResponseDto> getTimer(
@UserId Long userId) {

return ApiResponse.success(Success.GET_TIMER_SUCCESS,mainService.getMainPage(userId) );
}
}
Loading
Loading