Skip to content

Commit

Permalink
Feat/sse (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
flowerdonk authored Aug 13, 2023
1 parent 56e7326 commit be0de2a
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 149 deletions.
2 changes: 2 additions & 0 deletions src/main/java/com/anywayclear/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws
.authorizeHttpRequests(authorize -> authorize
.antMatchers(HttpMethod.OPTIONS).permitAll() // OPTIONS 메서드는 모두 허용
// .antMatchers("/api/members/**").authenticated() // jwt없이 요청한건지 재확인 가능
.antMatchers("/api/subscribes/**").authenticated() // jwt없이 요청한건지 재확인 가능
.antMatchers("/api/dibs/**").authenticated() // jwt없이 요청한건지 재확인 가능
.anyRequest().permitAll()
);

Expand Down
35 changes: 2 additions & 33 deletions src/main/java/com/anywayclear/controller/AlarmController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,10 @@

import com.anywayclear.dto.response.AlarmResponseList;
import com.anywayclear.service.AlarmService;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.time.LocalDateTime;

@RestController
@RequiredArgsConstructor
Expand All @@ -27,29 +15,10 @@ public class AlarmController {
// 알림 서비스
private final AlarmService alarmService;

@PutMapping("/topic/{topicName}")
public ResponseEntity<Void> createTopic(@PathVariable String topicName) {
final String topic = alarmService.createTopic(topicName);
return ResponseEntity.created(URI.create("api/alarms/" + topic)).build();
}

@GetMapping(value = "/topic/{topicName}/subscribe", produces = "text/event-stream")
public ResponseEntity<SseEmitter> subscribeTopic(@PathVariable String topicName, @AuthenticationPrincipal OAuth2User oAuth2User,
@RequestHeader(value = "Last-Event_ID", required = false) String lastEventId, HttpServletResponse response) {
System.out.println("OAuth UserName : " + oAuth2User);
return new ResponseEntity<>(alarmService.subscribeTopic(topicName, "username", lastEventId, LocalDateTime.now()), HttpStatus.OK);
}

@PostMapping("/topic/{topicName}")
@ResponseStatus(HttpStatus.CREATED)
public void pushAlarm(@PathVariable String topicName, @RequestParam(name = "sender") String sender, @RequestParam(name = "context") String context) {
alarmService.pushAlarm(topicName, sender, context);
}

@DeleteMapping("/topic/{topicName}")
@ResponseStatus(HttpStatus.OK)
public void deleteTopic(@PathVariable String topicName) {
alarmService.deleteTopic(topicName);
public void pushAlarm(@PathVariable String topicName, @RequestParam(name = "context") String context) {
alarmService.pushAlarm(topicName, context);
}

@GetMapping("/{memberId}/subs")
Expand Down
24 changes: 14 additions & 10 deletions src/main/java/com/anywayclear/controller/DibController.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package com.anywayclear.controller;

import com.anywayclear.dto.request.DibCreateRequest;
import com.anywayclear.dto.response.DibResponse;
import com.anywayclear.dto.response.DibResponseList;
import com.anywayclear.dto.response.IsDibResponse;
import com.anywayclear.dto.response.MultiResponse;
import com.anywayclear.entity.Dib;
import com.anywayclear.service.DibService;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.validation.Valid;
import java.net.URI;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;

@RestController
@RequestMapping("/api/dibs")
Expand All @@ -27,10 +25,10 @@ public DibController(DibService dibService) {
this.dibService = dibService;
}

@PostMapping
public ResponseEntity<Void> createDib(@Valid @RequestBody DibCreateRequest request) {
final Long id = dibService.createDib(request);
return ResponseEntity.created(URI.create("api/dibs/" + id)).build();
@GetMapping(value = "/{produceId}/dib", produces = "text/event-stream")
public ResponseEntity<SseEmitter> createDib(@PathVariable Long produceId, @AuthenticationPrincipal OAuth2User oAuth2User,
@RequestHeader(value = "Last-Event_ID", required = false) String lastEventId, HttpServletResponse response) {
return new ResponseEntity<>(dibService.createDib(produceId, oAuth2User, lastEventId, LocalDateTime.now()), HttpStatus.OK);
}

@GetMapping("/{userId}")
Expand All @@ -44,4 +42,10 @@ public ResponseEntity<IsDibResponse> getIsDib(@AuthenticationPrincipal OAuth2Use
return ResponseEntity.ok(dibService.getIsDib(userId, produceId));
}

@DeleteMapping("/{produce-id}")
@ResponseStatus(HttpStatus.OK)
public void deleteSubscribe(@PathVariable("produce-id") Long produceId, @AuthenticationPrincipal OAuth2User oAuth2User) {
String consumerId = (String) oAuth2User.getAttributes().get("userId");
dibService.deleteDib(produceId, consumerId);
}
}
23 changes: 16 additions & 7 deletions src/main/java/com/anywayclear/controller/SubscribeController.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package com.anywayclear.controller;

import com.anywayclear.dto.request.SubscribeCreateRequest;
import com.anywayclear.dto.response.IsSubResponse;
import com.anywayclear.dto.response.SubscribeResponseList;
import com.anywayclear.service.SubscribeService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.validation.Valid;
import java.net.URI;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;

@RestController
@RequestMapping("/api/subscribes")
Expand All @@ -21,10 +22,11 @@ public SubscribeController(SubscribeService subscribeService) {
this.subscribeService = subscribeService;
}

@PostMapping
public ResponseEntity<Void> createSubscribe(@Valid @RequestBody SubscribeCreateRequest request) {
final Long id = subscribeService.createSubscribe(request);
return ResponseEntity.created(URI.create("api/subscribes/" + id)).build();
@GetMapping(value = "/{userId}/subscribe", produces = "text/event-stream")
public ResponseEntity<SseEmitter> createSubscribe(@PathVariable String userId, @AuthenticationPrincipal OAuth2User oAuth2User,
@RequestHeader(value = "Last-Event_ID", required = false) String lastEventId, HttpServletResponse response) {
System.out.println("로그인 유저 = " + oAuth2User);
return new ResponseEntity<>(subscribeService.createSubscribe(userId, oAuth2User, lastEventId, LocalDateTime.now()), HttpStatus.OK);
}

@GetMapping
Expand All @@ -37,4 +39,11 @@ public ResponseEntity<IsSubResponse> getIsSub(@AuthenticationPrincipal OAuth2Use
String consumerId = (String) oAuth2User.getAttributes().get("userId");
return ResponseEntity.ok(subscribeService.getIsSub(consumerId,sellerId));
}

@DeleteMapping("/{seller-id}")
@ResponseStatus(HttpStatus.OK)
public void deleteSubscribe(@PathVariable("seller-id") String sellerId, @AuthenticationPrincipal OAuth2User oAuth2User) {
String consumerId = (String) oAuth2User.getAttributes().get("userId");
subscribeService.deleteSubscribe(sellerId, consumerId);
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/anywayclear/entity/Dib.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@
@Getter
@Setter
@NoArgsConstructor
@Table(
name = "dib",
uniqueConstraints = {
@UniqueConstraint(
name = "UniqueConsumerAndProduce",
columnNames = {
"consumer_id",
"produce_id"
}
)
}
)
public class Dib {

@Id
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/anywayclear/entity/Subscribe.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@
@Getter
@Setter
@NoArgsConstructor
@Table(
name = "subscribe",
uniqueConstraints = {
@UniqueConstraint(
name = "UniqueConsumerAndSeller",
columnNames = {
"consumer_id",
"seller_id"
}
)
}
)
public class Subscribe {

@Id
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/anywayclear/exception/ExceptionCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ public enum ExceptionCode {
INVALID_PRODUCE(BAD_REQUEST, "잘못된 농산물 정보 입니다", 400),
INVALID_PRODUCE_ID(BAD_REQUEST, "잘못된 농산물 ID 입니다", 400),
INVALID_AUCTION_ID(BAD_REQUEST, "잘못된 경매 ID 입니다", 400),
INVALID_DIB_ID(BAD_REQUEST, "잘못된 찜 ID 입니다", 400),
INVALID_SUBSCRIBE_ID(BAD_REQUEST, "잘못된 구독 ID 입니다", 400),
INVALID_PRICE(BAD_REQUEST, "현재 입찰가보다 낮게 입찰할 수 없습니다", 400),
EXPIRED_AUCTION_TIME(BAD_REQUEST,"경매 시간이 종료되었습니다.",400),
INVALID_AUCTION_STATUS(BAD_REQUEST, "경매 가능한 상태가 아닙니다", 400),
INVALID_INSERT_REQUEST(BAD_REQUEST, "중복된 Insert 요청입니다.", 400),

// 401 UNAUTHORIZED : 인증되지 않은 사용자
INVALID_TOKEN(UNAUTHORIZED, "잘못된 토큰입니다", 401),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolationException;

import java.sql.SQLIntegrityConstraintViolationException;

import static com.anywayclear.exception.ExceptionCode.*;
import static org.springframework.http.HttpStatus.*;
import static org.springframework.web.client.HttpClientErrorException.Forbidden;
Expand Down Expand Up @@ -56,4 +60,10 @@ private ErrorResponse handleResourceException() {
// private ErrorResponse handleException(Exception e) {
// return new ErrorResponse(INTERNAL_SERVER_ERROR,e.getMessage());
// }

@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
@ResponseStatus(BAD_REQUEST)
private ErrorResponse handleConstraintViolationException() {
return ErrorResponse.of(INVALID_INSERT_REQUEST);
}
}
5 changes: 4 additions & 1 deletion src/main/java/com/anywayclear/repository/DibRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;

public interface DibRepository extends JpaRepository<Dib, Long> {

Page<Dib> findAllByConsumer(Member member, Pageable pageable); // 찜 중인 농산물 찾기 (페이지)

List<Dib> findAllByConsumer(Optional<Member> member); // 찜 중인 농산물 찾기
List<Dib> findAllByConsumer(Member member); // 찜 중인 농산물 찾기

List<Dib> findAllByProduce(Produce produce); // 해당 농산물을 찜한 소비자 찾기

Optional<Dib> findByConsumerAndProduce(Member member, Produce produce);

void deleteByConsumerAndProduce(Member member, Produce produce);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ public Optional<SseEmitter> get(String key) {
return Optional.ofNullable(sseEmitterMap.get(key));
}

public List<SseEmitter> getListByKeyPrefix(String keyPrefix){
return sseEmitterMap.keySet().stream()
.filter(key -> key.startsWith(keyPrefix))
.map(sseEmitterMap::get)
.collect(Collectors.toList());
public Map<String, SseEmitter> getListByKeyPrefix(String keyPrefix){
// return sseEmitterMap.keySet().stream()
// .filter(key -> key.startsWith(keyPrefix))
// .map(sseEmitterMap::get)
// .collect(Collectors.toList());
return sseEmitterMap.entrySet().stream()
.filter(entry -> entry.getKey().startsWith(keyPrefix))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

public List<String> getKeyListByKeyPrefix(String keyPrefix){
Expand All @@ -34,6 +37,16 @@ public List<String> getKeyListByKeyPrefix(String keyPrefix){
.collect(Collectors.toList());
}

public Map<String, SseEmitter> getListByKeySuffix(String keySuffic){
// return sseEmitterMap.keySet().stream()
// .filter(key -> key.startsWith(keyPrefix))
// .map(sseEmitterMap::get)
// .collect(Collectors.toList());
return sseEmitterMap.entrySet().stream()
.filter(entry -> entry.getKey().endsWith(keySuffic))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

public void remove(String key) {
sseEmitterMap.remove(key);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface SSERepository {

Optional<SseEmitter> get(String key);

List<SseEmitter> getListByKeyPrefix(String keyPrefix);
Map<String, SseEmitter> getListByKeyPrefix(String keyPrefix);

List<String> getKeyListByKeyPrefix(String keyPrefix);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.anywayclear.entity.Subscribe;
import org.springframework.data.jpa.repository.JpaRepository;

import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;

Expand All @@ -14,4 +15,5 @@ public interface SubscribeRepository extends JpaRepository<Subscribe, Long> {

Optional<Subscribe> findByConsumerAndSeller(Member consumer, Member seller);

void deleteByConsumerAndSeller(Member consumer, Member seller);
}
Loading

0 comments on commit be0de2a

Please sign in to comment.