Skip to content

Commit

Permalink
Merge pull request #664 from woowacourse-teams/develop
Browse files Browse the repository at this point in the history
v1.2.1 배포 🙇🏻‍♂️
  • Loading branch information
kkojae91 authored Oct 21, 2022
2 parents d77dcdd + 9651fdf commit 2151be9
Show file tree
Hide file tree
Showing 151 changed files with 4,585 additions and 1,143 deletions.
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,33 @@
## 📎 바로가기

- 📚 [API 문서](https://dev.jupjup.site/docs)
- 🎁 [스토리북](https://62e64dc73aafd7bc9338ba73-fihzwjkqkx.chromatic.com)
- 🎁 [스토리북](https://62e64dc73aafd7bc9338ba73-imzhfpkupu.chromatic.com/)
- 🐹 [팀문화](https://selective-archeology-e38.notion.site/858f167439b94c9caee71ab177bce08e)
- 🌎 [깃헙 위키](https://github.com/woowacourse-teams/2022-pickpick/wiki)

<br>
<br>

## 팀원 소개 👩🏻‍💻🧑🏻‍💻

| [🐈‍⬛ 호프](https://github.com/moonheekim0118) | [👍 꼬재](https://github.com/kkojae91) | [🌱 봄](https://github.com/JangBomi) | [🏝 써머](https://github.com/hyewoncc) | [🪁 연로그](https://github.com/yeon-06) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
| [🐈‍⬛ 호프](https://github.com/moonheekim0118) | [👍 꼬재](https://github.com/kkojae91) | [🌱 봄](https://github.com/JangBomi) | [🏝 써머](https://github.com/hyewoncc) | [🪁 연로그](https://github.com/yeon-06) |
| :-------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: |
| <a href="https://github.com/moonheekim0118"> <img src="https://avatars.githubusercontent.com/u/61469664?v=4" width=200px alt="_"/> </a> | <a href="https://github.com/kkojae91"> <img src="https://avatars.githubusercontent.com/u/68001045?v=4" width=200px alt="_"/> </a> | <a href="https://github.com/JangBomi"> <img src="https://avatars.githubusercontent.com/u/55357130?v=4" width=200px alt="_"/> </a> | <a href="https://github.com/hyewoncc"> <img src="https://avatars.githubusercontent.com/u/80666066?v=4" width=200px alt="_"/> </a> | <a href="https://github.com/yeon-06"> <img src="https://avatars.githubusercontent.com/u/53105735?v=4" width=200px alt="_"> |
| 프론트엔드 | 프론트엔드 | 백엔드 | 백엔드 | 백엔드 |
| 프론트엔드 | 프론트엔드 | 백엔드 | 백엔드 | 백엔드 |

<br>

## 프론트엔드 기술 스택 ✨

![프론트엔드기술스택](https://user-images.githubusercontent.com/61469664/196941679-067588a0-d9e0-4afa-8ad8-18a07abfb70a.png)

<br>

## 백엔드 기술 스택 ⚡️

![백엔드기술스택](https://user-images.githubusercontent.com/61469664/196941717-8993f980-00fb-47f7-ba7f-bc12fbc70677.png)

<br>

## 인프라 기술 스택 ⚙️

![인프라](https://user-images.githubusercontent.com/61469664/196941755-3ca4cd9b-81ce-4301-a549-d8f6d7198980.png)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.pickpick.auth.ui.dto.LoginResponse;
import com.pickpick.channel.domain.Channel;
import com.pickpick.channel.domain.ChannelRepository;
import com.pickpick.exception.workspace.WorkspaceDuplicateException;
import com.pickpick.member.domain.Member;
import com.pickpick.member.domain.MemberRepository;
import com.pickpick.support.ExternalClient;
Expand All @@ -23,16 +24,16 @@ public class AuthService {
private final MemberRepository members;
private final WorkspaceRepository workspaces;
private final ChannelRepository channels;
private final ExternalClient slackClient;
private final ExternalClient externalClient;
private final JwtTokenProvider jwtTokenProvider;

public AuthService(final MemberRepository members, final WorkspaceRepository workspaces,
final ChannelRepository channels, final ExternalClient slackClient,
final ChannelRepository channels, final ExternalClient externalClient,
final JwtTokenProvider jwtTokenProvider) {
this.members = members;
this.workspaces = workspaces;
this.channels = channels;
this.slackClient = slackClient;
this.externalClient = externalClient;
this.jwtTokenProvider = jwtTokenProvider;
}

Expand All @@ -41,28 +42,39 @@ public void verifyToken(final String token) {
}

@Transactional
public LoginResponse registerWorkspace(final String code) {
WorkspaceInfoDto workspaceInfoDto = slackClient.callWorkspaceInfo(code);
public void registerWorkspace(final String code) {
WorkspaceInfoDto workspaceInfoDto = externalClient.callWorkspaceInfo(code);

validateExistWorkspace(workspaceInfoDto.getWorkspaceSlackId());

Workspace workspace = workspaces.save(workspaceInfoDto.toEntity());

List<Member> allWorkspaceMembers = slackClient.findMembersByWorkspace(workspace);
List<Member> allWorkspaceMembers = externalClient.findMembersByWorkspace(workspace);
members.saveAll(allWorkspaceMembers);

List<Channel> allWorkspaceChannels = slackClient.findChannelsByWorkspace(workspace);
List<Channel> allWorkspaceChannels = externalClient.findChannelsByWorkspace(workspace);
channels.saveAll(allWorkspaceChannels);
}

return login(code);
private void validateExistWorkspace(final String workspaceSlackId) {
if (workspaces.existsBySlackId(workspaceSlackId)) {
throw new WorkspaceDuplicateException(workspaceSlackId);
}
}

@Transactional
public LoginResponse login(final String code) {
String userToken = slackClient.callUserToken(code);
String memberSlackId = slackClient.callMemberSlackId(userToken);
String userToken = externalClient.callUserToken(code);
return loginByToken(userToken);
}

private LoginResponse loginByToken(final String userSlackToken) {
String memberSlackId = externalClient.callMemberSlackId(userSlackToken);

Member member = members.getBySlackId(memberSlackId);

boolean isFirstLogin = member.isFirstLogin();
member.firstLogin(userToken);
member.firstLogin(userSlackToken);

return LoginResponse.builder()
.token(jwtTokenProvider.createToken(String.valueOf(member.getId())))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ public class WorkspaceInfoDto {
private final String workspaceSlackId;
private final String botToken;
private final String botSlackId;
private final String userToken;

public WorkspaceInfoDto(final String workspaceSlackId, final String botToken, final String botSlackId) {
public WorkspaceInfoDto(final String workspaceSlackId, final String botToken, final String botSlackId,
final String userToken) {
this.workspaceSlackId = workspaceSlackId;
this.botToken = botToken;
this.botSlackId = botSlackId;
this.userToken = userToken;
}

public Workspace toEntity() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public LoginResponse login(@RequestParam @NotEmpty final String code) {
}

@GetMapping("/slack-workspace")
public LoginResponse registerWorkspace(@RequestParam @NotEmpty final String code) {
return authService.registerWorkspace(code);
public void registerWorkspace(@RequestParam @NotEmpty final String code) {
authService.registerWorkspace(code);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ public ChannelResponses findByWorkspace(final Long memberId) {
}

private void validateToken(final Member member) {
String token = member.getToken();
String token = member.getSlackToken();
if (StringUtils.isNullOrEmpty(token)) {
throw new MemberTokenNotFoundException(member.getId(), token);
}
}

private List<Channel> findParticipatingChannels(final Member member) {
String token = member.getToken();
String token = member.getSlackToken();
Participation participation = externalClient.findChannelParticipation(token);

Workspace workspace = member.getWorkspace();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ public class Channel {
protected Channel() {
}

public Channel(final String slackId, final String name) {
this.slackId = slackId;
this.name = name;
}

public Channel(final String slackId, final String name, final Workspace workspace) {
this.slackId = slackId;
this.name = name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public interface ChannelSubscriptionRepository extends Repository<ChannelSubscri

void deleteAllByChannelAndMember(Channel channel, Member member);

void deleteAllByChannelSlackId(String channelSlackId);

default ChannelSubscription getFirstByMemberIdOrderByViewOrderAsc(final Long memberId) {
return findFirstByMemberIdOrderByViewOrderAsc(memberId)
.orElseThrow(() -> new SubscriptionNotFoundException(memberId));
Expand Down
11 changes: 8 additions & 3 deletions backend/src/main/java/com/pickpick/config/SlackProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ public class SlackProperties {
private final String clientSecret;

@NotBlank
private final String redirectUrl;
private final String loginRedirectUrl;

public SlackProperties(final String clientId, final String clientSecret, final String redirectUrl) {
@NotBlank
private final String workspaceRedirectUrl;

public SlackProperties(final String clientId, final String clientSecret, final String loginRedirectUrl,
final String workspaceRedirectUrl) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.redirectUrl = redirectUrl;
this.loginRedirectUrl = loginRedirectUrl;
this.workspaceRedirectUrl = workspaceRedirectUrl;
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package com.pickpick.exception;

import com.slack.api.methods.SlackApiTextResponse;

public class SlackApiCallException extends RuntimeException {

private static final String DEFAULT_MESSAGE = "슬랙 API 호출 실패";

public SlackApiCallException(final Exception e) {
super(e);
}

public SlackApiCallException(final String apiMethod) {
super(String.format("%s -> api method : %s", DEFAULT_MESSAGE, apiMethod));
}

public SlackApiCallException(final String apiMethod, final String slackErrorMessage) {
super(String.format("%s -> api method : %s, slack message : %S", DEFAULT_MESSAGE, apiMethod,
slackErrorMessage));
public SlackApiCallException(final String apiMethod, final SlackApiTextResponse response) {
super(String.format("%s -> api method : %s, slack message : %s, slack needed: %s, slack provided: %s",
DEFAULT_MESSAGE,
apiMethod,
response.getError(),
response.getNeeded(),
response.getProvided()
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.pickpick.exception.workspace;

import com.pickpick.exception.BadRequestException;

public class WorkspaceDuplicateException extends BadRequestException {

private static final String ERROR_CODE = "WORKSPACE_DUPLICATE";
private static final String SERVER_MESSAGE = "이미 존재하는 워크스페이스 등록 시도";
private static final String CLIENT_MESSAGE = "이미 등록된 워크스페이스입니다.";

public WorkspaceDuplicateException(final String slackId) {
super(String.format("%s -> workspace slack id: %s", SERVER_MESSAGE, slackId), CLIENT_MESSAGE, ERROR_CODE);
}
}
12 changes: 3 additions & 9 deletions backend/src/main/java/com/pickpick/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class Member {
private boolean isFirstLogin = true;

@Column(name = "token", length = 256, unique = true)
private String token;
private String slackToken;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "workspace_id")
Expand All @@ -46,22 +46,16 @@ public class Member {
protected Member() {
}

public Member(final String slackId, final String username, final String thumbnailUrl) {
this.slackId = slackId;
this.username = username;
this.thumbnailUrl = thumbnailUrl;
}

public Member(final String slackId, final String username, final String thumbnailUrl, final Workspace workspace) {
this.slackId = slackId;
this.username = username;
this.thumbnailUrl = thumbnailUrl;
this.workspace = workspace;
}

public void firstLogin(final String token) {
public void firstLogin(final String slackToken) {
this.isFirstLogin = false;
this.token = token;
this.slackToken = slackToken;
}

public void update(final String username, final String thumbnailUrl) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.pickpick.member.domain;

import com.pickpick.exception.member.MemberNotFoundException;
import com.pickpick.workspace.domain.Workspace;
import java.util.List;
import java.util.Optional;
import org.springframework.data.repository.Repository;

Expand All @@ -14,6 +16,8 @@ public interface MemberRepository extends Repository<Member, Long> {

Optional<Member> findBySlackId(String slackId);

List<Member> findAllByWorkspace(Workspace workspace);

default Member getById(final Long id) {
return findById(id)
.orElseThrow(() -> new MemberNotFoundException(id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
import com.pickpick.message.domain.Message;
import com.pickpick.message.domain.MessageRepository;
import com.pickpick.message.domain.QBookmark;
import com.pickpick.message.support.SlackIdExtractor;
import com.pickpick.message.ui.dto.BookmarkFindRequest;
import com.pickpick.message.ui.dto.BookmarkRequest;
import com.pickpick.message.ui.dto.BookmarkResponse;
import com.pickpick.message.ui.dto.BookmarkResponses;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -23,17 +26,24 @@
@Service
public class BookmarkService {

private static final String MENTION_PREFIX = "<@";
private static final String MENTION_SUFFIX = ">";
private static final String MENTION_MARK = "@";

private final BookmarkRepository bookmarks;
private final MessageRepository messages;
private final MemberRepository members;
private final JPAQueryFactory jpaQueryFactory;
private final SlackIdExtractor slackIdExtractor;

public BookmarkService(final BookmarkRepository bookmarks, final MessageRepository messages,
final MemberRepository members, final JPAQueryFactory jpaQueryFactory) {
final MemberRepository members, final JPAQueryFactory jpaQueryFactory,
final SlackIdExtractor slackIdExtractor) {
this.bookmarks = bookmarks;
this.messages = messages;
this.members = members;
this.jpaQueryFactory = jpaQueryFactory;
this.slackIdExtractor = slackIdExtractor;
}

@Transactional
Expand All @@ -49,7 +59,10 @@ public void save(final Long memberId, final BookmarkRequest bookmarkRequest) {
public BookmarkResponses find(final BookmarkFindRequest request, final Long memberId) {
List<Bookmark> bookmarkList = findBookmarks(request, memberId);

return new BookmarkResponses(toBookmarkResponseList(bookmarkList), hasPast(bookmarkList, memberId));
List<BookmarkResponse> responses = toBookmarkResponseList(bookmarkList);
replaceMentionMembers(memberId, responses);

return new BookmarkResponses(responses, hasPast(bookmarkList, memberId));
}

private List<Bookmark> findBookmarks(final BookmarkFindRequest request, final Long memberId) {
Expand Down Expand Up @@ -102,6 +115,29 @@ private BooleanExpression meetHasPastCondition(final List<Bookmark> bookmarkList
return QBookmark.bookmark.createdDate.before(targetBookmark.getCreatedDate());
}

private void replaceMentionMembers(final Long memberId, final List<BookmarkResponse> bookmarkResponses) {
Member member = members.getById(memberId);
List<Member> workspaceMembers = members.findAllByWorkspace(member.getWorkspace());

Map<String, String> memberNames = workspaceMembers.stream()
.collect(Collectors.toMap(Member::getSlackId,
workspaceMember -> MENTION_MARK + workspaceMember.getUsername()));

for (BookmarkResponse response : bookmarkResponses) {
String text = replaceMentionMemberInText(response.getText(), memberNames);
response.replaceText(text);
}
}

private String replaceMentionMemberInText(String text, final Map<String, String> memberMap) {
Set<String> slackIds = slackIdExtractor.extract(text);
for (String slackId : slackIds) {
String mention = MENTION_PREFIX + slackId + MENTION_SUFFIX;
text = text.replace(mention, memberMap.getOrDefault(slackId, mention));
}
return text;
}

@Transactional
public void delete(final Long messageId, final Long memberId) {
Bookmark bookmark = bookmarks.findByMessageIdAndMemberId(messageId, memberId)
Expand Down
Loading

0 comments on commit 2151be9

Please sign in to comment.