Skip to content

Commit

Permalink
Merge pull request #940 from woowacourse-teams/develop
Browse files Browse the repository at this point in the history
Release 1.6.2
  • Loading branch information
pilyang authored Feb 20, 2024
2 parents 9c84017 + acc2d6b commit 9a071c7
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public class FeedWriteService {
private final FeedRepository feedRepository;
private final FeedThreadImageRepository feedThreadImageRepository;
private final MemberRepository memberRepository;
private final MemberTeamPlaceRepository memberTeamPlaceRepository;
private final FileStorageManager fileStorageManager;

@Value("${aws.s3.image-directory}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
Expand All @@ -27,6 +28,7 @@
import team.teamby.teambyteam.notice.application.dto.NoticeResponse;
import team.teamby.teambyteam.notice.domain.Notice;
import team.teamby.teambyteam.notice.domain.NoticeRepository;
import team.teamby.teambyteam.notice.domain.event.NoticeCreationEvent;
import team.teamby.teambyteam.notice.domain.image.NoticeImage;
import team.teamby.teambyteam.notice.domain.image.NoticeImageRepository;
import team.teamby.teambyteam.notice.domain.image.vo.ImageName;
Expand All @@ -52,6 +54,7 @@ public class NoticeService {

private final Clock clock;

private final ApplicationEventPublisher applicationEventPublisher;
private final NoticeRepository noticeRepository;
private final TeamPlaceRepository teamPlaceRepository;
private final MemberRepository memberRepository;
Expand All @@ -75,9 +78,10 @@ public Long register(final NoticeRegisterRequest noticeRegisterRequest,
final Notice savedNotice = noticeRepository.save(new Notice(contentVo, teamPlaceId, memberId.id()));
saveImages(images, savedNotice);

Long savedNoticeId = savedNotice.getId();
final Long savedNoticeId = savedNotice.getId();
log.info("공지 등록 - 등록자 이메일 : {}, 팀플레이스 아이디 : {}, 공지 아이디 : {}", memberEmailDto.email(), teamPlaceId,
savedNoticeId);
applicationEventPublisher.publishEvent(new NoticeCreationEvent(savedNotice));
return savedNoticeId;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ public interface NoticeRepository extends JpaRepository<Notice, Long> {
"LIMIT 1"
)
Optional<Notice> findMostRecentByTeamPlaceId(Long teamPlaceId);

@Query("SELECT n.teamPlaceId FROM Notice n WHERE n.id = :id")
Optional<Long> findTeamPlaceIdByNoticeId(Long id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package team.teamby.teambyteam.notice.domain.event;

import team.teamby.teambyteam.common.domain.DomainEvent;
import team.teamby.teambyteam.notice.domain.Notice;

public class NoticeCreationEvent implements DomainEvent<Long> {

public static final String NOTICE_NOT_CREATED_MESSAGE_FORMAT = "아직 생성되지 않은 공지입니다. teamplaceId: %d";
private final Long id;

public NoticeCreationEvent(final Notice notice) {
validate(notice);
this.id = notice.getId();
}

private static void validate(Notice notice) {
if(notice.getId() == null) {
throw new RuntimeException(String.format(NOTICE_NOT_CREATED_MESSAGE_FORMAT, notice.getTeamPlaceId()));
}
}

@Override
public Long getDomainId() {
return id;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.transaction.event.TransactionalEventListener;
import team.teamby.teambyteam.common.domain.DomainEvent;
import team.teamby.teambyteam.feed.application.event.FeedEvent;
import team.teamby.teambyteam.notice.domain.event.NoticeCreationEvent;
import team.teamby.teambyteam.sse.domain.TeamPlaceEventId;
import team.teamby.teambyteam.sse.domain.TeamPlaceSseEvent;
import team.teamby.teambyteam.sse.domain.converter.TeamPlaceEventConvertMapper;
Expand All @@ -22,7 +23,10 @@ public class TeamPlaceSsePublisher {

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,
classes = {FeedEvent.class}
classes = {
FeedEvent.class,
NoticeCreationEvent.class
}
)
public void publishEvent(final DomainEvent domainEvent) {
final TeamPlaceSseEvent teamPlaceSseEvent = eventConvertMapper.convert(domainEvent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@
@Slf4j
@Component
@RequiredArgsConstructor
public class FeedEventConverter implements TeamPlaceSseConverter {
public class FeedEventConverter implements TeamPlaceSseConverter<Long> {

private final FeedRepository feedRepository;
private final MemberTeamPlaceRepository memberTeamPlaceRepository;

@Override
@Transactional(readOnly = true)
public TeamPlaceSseEvent convert(final DomainEvent event) {
final Long feedId = (Long) event.getDomainId();
public TeamPlaceSseEvent convert(final DomainEvent<Long> event) {
final Long feedId = event.getDomainId();
final Feed feed = feedRepository.findById(feedId)
.orElseThrow(() -> {
final String message = "No FeedFound ID : " + feedId;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package team.teamby.teambyteam.sse.domain.converter;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import team.teamby.teambyteam.common.domain.DomainEvent;
import team.teamby.teambyteam.notice.domain.NoticeRepository;
import team.teamby.teambyteam.notice.domain.event.NoticeCreationEvent;
import team.teamby.teambyteam.sse.domain.TeamPlaceSseEvent;
import team.teamby.teambyteam.sse.domain.emitter.TeamPlaceEmitterId;

@Component
@RequiredArgsConstructor
public class NoticeCreatedEventConverter implements TeamPlaceSseConverter<Long> {

private final NoticeRepository noticeRepository;

@Override
public TeamPlaceSseEvent convert(DomainEvent<Long> event) {
final Long noticeId = event.getDomainId();
final Long teamPlaceId = noticeRepository.findTeamPlaceIdByNoticeId(noticeId)
.orElseThrow(() -> new RuntimeException(String.format("팀플레이스 공지를 찾을 수 없습니다. id : %d", noticeId)));
return new NoticeCreatedSse(noticeId, teamPlaceId);
}

@Override
public String supportEventName() {
return NoticeCreationEvent.class.getName();
}

private static class NoticeCreatedSse implements TeamPlaceSseEvent {

private static final String EVENT_NAME = "new_notice";

private final NoticeSse event;

public NoticeCreatedSse(final Long id, final Long teamPlaceId) {
this.event = new NoticeSse(id, teamPlaceId);
}

@Override
public Long getTeamPlaceId() {
return event.teamPlaceId;
}

@Override
public String getEventName() {
return EVENT_NAME;
}

@Override
public Object getEvent(TeamPlaceEmitterId emitterId) {
return event;
}

private record NoticeSse(Long id, Long teamPlaceId) {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import team.teamby.teambyteam.common.domain.DomainEvent;
import team.teamby.teambyteam.sse.domain.TeamPlaceSseEvent;

public interface TeamPlaceSseConverter {
TeamPlaceSseEvent convert(DomainEvent event);
public interface TeamPlaceSseConverter<T> {
TeamPlaceSseEvent convert(DomainEvent<T> event);

String supportEventName();
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import team.teamby.teambyteam.notice.application.dto.NoticeResponse;
import team.teamby.teambyteam.notice.domain.Notice;
import team.teamby.teambyteam.notice.domain.NoticeRepository;
import team.teamby.teambyteam.notice.domain.event.NoticeCreationEvent;
import team.teamby.teambyteam.teamplace.domain.TeamPlace;
import team.teamby.teambyteam.teamplace.exception.TeamPlaceException.NotFoundException;

Expand Down Expand Up @@ -93,6 +94,23 @@ void success() {
assertThat(registeredId).isNotNull();
}

@Test
@DisplayName("공지 등록 이벤트를 발행한다")
void publishCreatedEvent() {
// given

// when
final Long registeredId = noticeService.register(request, teamPlace.getId(), memberEmailDto);

// then
final Optional<NoticeCreationEvent> event = applicationEvents.stream(NoticeCreationEvent.class).findAny();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(event).isNotEmpty();
softly.assertThat(event.get().getDomainId()).isEqualTo(registeredId);
});

}

@Test
@DisplayName("공지 등록 시 팀 플레이스 ID에 해당하는 팀 플레이스가 존재하지 않으면 예외가 발생한다.")
void failTeamPlaceNotExistById() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ public TeamPlaceSseConverter testConvertor() {
return new TestSseConverter();
}

public static class TestSseConverter implements TeamPlaceSseConverter {
public static class TestSseConverter implements TeamPlaceSseConverter<Long> {

@Override
public TeamPlaceSseEvent convert(DomainEvent event) {
public TeamPlaceSseEvent convert(DomainEvent<Long> event) {
return ((TestDomainEvent) event).getTestSseEvent();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package team.teamby.teambyteam.sse.domain.converter;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import team.teamby.teambyteam.common.builder.TestFixtureBuilder;
import team.teamby.teambyteam.common.fixtures.MemberFixtures;
import team.teamby.teambyteam.common.fixtures.NoticeFixtures;
import team.teamby.teambyteam.common.fixtures.TeamPlaceFixtures;
import team.teamby.teambyteam.member.domain.Member;
import team.teamby.teambyteam.notice.domain.Notice;
import team.teamby.teambyteam.notice.domain.event.NoticeCreationEvent;
import team.teamby.teambyteam.sse.domain.TeamPlaceSseEvent;
import team.teamby.teambyteam.teamplace.domain.TeamPlace;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@Sql({"/h2-truncate.sql"})
class NoticeCreatedEventConverterTest {

@Autowired
private NoticeCreatedEventConverter noticeCreatedEventConverter;

@Autowired
private TestFixtureBuilder testFixtureBuilder;

@Test
@DisplayName("NoticeCreatedEvent 지원 확인")
void isSupportNoticeCreatedEvent() {
// given
final String expected = NoticeCreationEvent.class.getName();

// when
final String actual = noticeCreatedEventConverter.supportEventName();

// then
assertThat(actual).isEqualTo(expected);
}

@Test
@DisplayName("Notice 이벤트 변환 테스트")
void convert() {
// given
final Member member = testFixtureBuilder.buildMember(MemberFixtures.ROY());
final TeamPlace teamPlace = testFixtureBuilder.buildTeamPlace(TeamPlaceFixtures.CONTROLS_TEAM_PLACE());
testFixtureBuilder.buildMemberTeamPlace(member, teamPlace);
final Notice createdNotice = testFixtureBuilder.buildNotice(NoticeFixtures.NOTICE_1ST(teamPlace.getId(), member.getId()));

final NoticeCreationEvent noticeCreationEvent = new NoticeCreationEvent(createdNotice);

// when
final TeamPlaceSseEvent convertedEvent = noticeCreatedEventConverter.convert(noticeCreationEvent);

// then
assertThat(convertedEvent.getTeamPlaceId()).isEqualTo(teamPlace.getId());

}
}
11 changes: 10 additions & 1 deletion frontend/src/hooks/queries/useFetchDailySchedules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@ export const useFetchDailySchedules = (
month: number,
day: number,
) => {
const stringYear = String(year).padStart(4, '0');
const stringMonth = String(month + 1).padStart(2, '0');
const stringDay = String(day).padStart(2, '0');

const { data } = useQuery(
['dailySchedules', year, month, day],
() => fetchSchedules(teamPlaceId, year, month + 1, day),
() =>
fetchSchedules(
teamPlaceId,
`${stringYear}${stringMonth}${stringDay}`,
`${stringYear}${stringMonth}${stringDay}`,
),
{
enabled: teamPlaceId > 0,
staleTime: STALE_TIME.DAILY_SCHEDULES,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/queries/useFetchThreads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const useFetchThreads = (teamPlaceId: number) => {
{
enabled: teamPlaceId > 0,
getNextPageParam: (lastPage) => {
if (lastPage.threads.length !== THREAD_SIZE) return undefined;
if (lastPage.threads.length < THREAD_SIZE) return undefined;
return lastPage.threads[THREAD_SIZE - 1].id;
},
staleTime: STALE_TIME.TEAM_FEED,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/TeamFeedPage/TeamFeedPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const TeamFeedPage = (props: TeamFeedPageProps) => {
: '여기에 채팅을 입력하세요. \n\nShift + Enter로 새 행을 추가합니다.'
}
maxLength={10000}
autoFocus
autoFocus={!isMobile}
readOnly={isSendingImage}
/>
<S.ButtonContainer $isMobile={isMobile}>
Expand Down

0 comments on commit 9a071c7

Please sign in to comment.