diff --git a/backend/src/main/java/heartbeat/enums/AssigneeFilterMethod.java b/backend/src/main/java/heartbeat/enums/AssigneeFilterMethod.java index 8b92a3b354..9903ff32d5 100644 --- a/backend/src/main/java/heartbeat/enums/AssigneeFilterMethod.java +++ b/backend/src/main/java/heartbeat/enums/AssigneeFilterMethod.java @@ -7,6 +7,8 @@ @AllArgsConstructor public enum AssigneeFilterMethod { + ASSIGNEE_FIELD_ID("assignee"), + LAST_ASSIGNEE("lastAssignee"), HISTORICAL_ASSIGNEE("historicalAssignee"); diff --git a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java index 827c654528..50bc79cc13 100644 --- a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java +++ b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java @@ -58,6 +58,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -494,9 +495,11 @@ private List getRealDoneCards(StoryPointsAndCycleTimeRequest reques } } futures.forEach(doneCard -> { - CycleTimeInfoDTO cycleTimeInfoDTO = getCycleTime(baseUrl, doneCard.getKey(), request.getToken(), - request.isTreatFlagCardAsBlock(), keyFlagged, request.getStatus()); - List assigneeSet = getAssigneeSet(request, baseUrl, filterMethod, doneCard); + CardHistoryResponseDTO cardHistoryResponseDTO = getCardAllHistory(baseUrl, doneCard.getKey(), 0, + request.getToken()); + CycleTimeInfoDTO cycleTimeInfoDTO = getCycleTime(cardHistoryResponseDTO, request.isTreatFlagCardAsBlock(), + keyFlagged, request.getStatus()); + List assigneeSet = getAssigneeSet(cardHistoryResponseDTO, filterMethod, doneCard); if (users.stream().anyMatch(assigneeSet::contains)) { JiraCardDTO jiraCardDTO = JiraCardDTO.builder() .baseInfo(doneCard) @@ -514,26 +517,40 @@ private List getRealDoneCards(StoryPointsAndCycleTimeRequest reques return realDoneCards; } - private List getAssigneeSet(StoryPointsAndCycleTimeRequest request, URI baseUrl, String filterMethod, + private List getAssigneeSet(CardHistoryResponseDTO jiraCardHistory, String filterMethod, JiraCard doneCard) { List assigneeSet = new ArrayList<>(); Assignee assignee = doneCard.getFields().getAssignee(); - if (assignee != null && useLastAssignee(filterMethod)) { - assigneeSet.add(assignee.getDisplayName()); + if (useLastAssignee(filterMethod)) { + if (assignee != null) { + assigneeSet.add(assignee.getDisplayName()); + } + else { + List historicalAssignees = getHistoricalAssignees(jiraCardHistory); + assigneeSet.add(getLastHistoricalAssignee(historicalAssignees)); + } } - - if (assignee != null && filterMethod.equals(AssigneeFilterMethod.HISTORICAL_ASSIGNEE.getDescription())) { - List historyDisplayName = getHistoricalAssignees(baseUrl, doneCard.getKey(), request.getToken()); - assigneeSet.addAll(historyDisplayName); + else { + List historicalAssignees = getHistoricalAssignees(jiraCardHistory); + assigneeSet.addAll(historicalAssignees); } return assigneeSet; } - private List getHistoricalAssignees(URI baseUrl, String cardKey, String token) { - CardHistoryResponseDTO jiraCardHistory = getCardAllHistory(baseUrl, cardKey, 0, token); - return jiraCardHistory.getItems().stream().map(item -> item.getActor().getDisplayName()).distinct().toList(); + private String getLastHistoricalAssignee(List historicalAssignees) { + return historicalAssignees.stream().filter(Objects::nonNull).findFirst().orElse(null); + } + + private List getHistoricalAssignees(CardHistoryResponseDTO jiraCardHistory) { + return jiraCardHistory.getItems() + .stream() + .filter(item -> AssigneeFilterMethod.ASSIGNEE_FIELD_ID.getDescription().equalsIgnoreCase(item.getFieldId())) + .sorted(Comparator.comparing(HistoryDetail::getTimestamp).reversed()) + .map(item -> item.getTo().getDisplayValue()) + .distinct() + .toList(); } private CardHistoryResponseDTO getCardAllHistory(URI baseUrl, String cardKey, int startAt, String token) { @@ -602,9 +619,8 @@ card, new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH 时").format(new Date(last }).isPresent(); } - private CycleTimeInfoDTO getCycleTime(URI baseUrl, String doneCardKey, String token, Boolean treatFlagCardAsBlock, + private CycleTimeInfoDTO getCycleTime(CardHistoryResponseDTO cardHistoryResponseDTO, Boolean treatFlagCardAsBlock, String keyFlagged, List realDoneStatus) { - CardHistoryResponseDTO cardHistoryResponseDTO = getCardAllHistory(baseUrl, doneCardKey, 0, token); List statusChangedArray = putStatusChangeEventsIntoAnArray(cardHistoryResponseDTO, keyFlagged); List cycleTimeInfos = boardUtil.getCycleTimeInfos(statusChangedArray, realDoneStatus, @@ -782,8 +798,10 @@ private List getMatchedNonDoneCards(StoryPointsAndCycleTimeRequest String keyFlagged = cardCustomFieldKey.getFlagged(); allNonDoneCards.forEach(card -> { - CycleTimeInfoDTO cycleTimeInfoDTO = getCycleTime(baseUrl, card.getKey(), request.getToken(), - request.isTreatFlagCardAsBlock(), keyFlagged, request.getStatus()); + CardHistoryResponseDTO cardHistoryResponseDTO = getCardAllHistory(baseUrl, card.getKey(), 0, + request.getToken()); + CycleTimeInfoDTO cycleTimeInfoDTO = getCycleTime(cardHistoryResponseDTO, request.isTreatFlagCardAsBlock(), + keyFlagged, request.getStatus()); List assigneeSet = getAssigneeSetWithDisplayName(baseUrl, card, request.getToken()); if (users.stream().anyMatch(assigneeSet::contains)) { diff --git a/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java b/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java index 87b28e0143..ac78f6acf7 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java @@ -499,13 +499,18 @@ public static StoryPointsAndCycleTimeRequest.StoryPointsAndCycleTimeRequestBuild public static AllDoneCardsResponseDTO.AllDoneCardsResponseDTOBuilder ALL_DONE_CARDS_RESPONSE_FOR_ASSIGNEE_FILTER_METHOD_TEST() { return AllDoneCardsResponseDTO.builder() - .total("2") + .total("3") .issues(List.of( new JiraCard("ADM-475", JiraCardField.builder() .assignee(new Assignee("da pei")) .status(new Status(CardStepsEnum.DONE.getValue())) .build()), + new JiraCard("ADM-520", + JiraCardField.builder() + .assignee(null) + .status(new Status(CardStepsEnum.DONE.getValue())) + .build()), new JiraCard("ADM-524", JiraCardField.builder() .assignee(new Assignee("xiao pei")) @@ -516,6 +521,8 @@ public static AllDoneCardsResponseDTO.AllDoneCardsResponseDTOBuilder ALL_DONE_CA public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD1_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD() { return CardHistoryResponseDTO.builder() .items(List.of( + new HistoryDetail(1673556350000L, "assignee", new Status("da pei"), new Status(null), + new HistoryDetail.Actor("da pei")), new HistoryDetail(1673556350000L, "status", new Status(TESTING), new Status(REVIEW), new HistoryDetail.Actor("da pei")), new HistoryDetail(1674556350000L, "status", new Status(DONE), new Status(TESTING), @@ -525,6 +532,19 @@ public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD1_HISTORY public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD2_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD() { return CardHistoryResponseDTO.builder() .items(List.of( + new HistoryDetail(1673556350000L, "assignee", new Status("da pei"), new Status(null), + new HistoryDetail.Actor("da pei")), + new HistoryDetail(1673556350000L, "status", new Status(TESTING), new Status(REVIEW), + new HistoryDetail.Actor("da pei")), + new HistoryDetail(1674556350000L, "status", new Status(DONE), new Status(TESTING), + new HistoryDetail.Actor("xiao pei")))); + } + + public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD3_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD() { + return CardHistoryResponseDTO.builder() + .items(List.of( + new HistoryDetail(1673556350000L, "assignee", new Status("da pei"), new Status(null), + new HistoryDetail.Actor("da pei")), new HistoryDetail(1673556350000L, "status", new Status(TESTING), new Status(REVIEW), new HistoryDetail.Actor("da pei")), new HistoryDetail(1674556350000L, "status", new Status(DONE), new Status(TESTING), diff --git a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java index 0ed50b5bf1..536bd09893 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java @@ -52,6 +52,7 @@ import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD1_HISTORY_FOR_MULTIPLE_STATUSES; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD2_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD2_HISTORY_FOR_MULTIPLE_STATUSES; +import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD3_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_DONE_TIME_GREATER_THAN_END_TIME_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_MULTI_RESPONSE_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_RESPONSE_BUILDER; @@ -877,12 +878,14 @@ void shouldGetRealDoneCardWhenCallGetStoryPointsAndCycleTimeWhenUseHistoricalAss .thenReturn(CARD1_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD().build()); when(jiraFeignClient.getJiraCardHistory(baseUrl, "ADM-524", 0, 100, token)) .thenReturn(CARD2_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD().build()); + when(jiraFeignClient.getJiraCardHistory(baseUrl, "ADM-520", 0, 100, token)) + .thenReturn(CARD3_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); // then CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards(request, jiraBoardSetting.getBoardColumns(), List.of("da pei"), assigneeFilter); - assertThat(cardCollection.getCardsNumber()).isEqualTo(2); + assertThat(cardCollection.getCardsNumber()).isEqualTo(3); } @Test @@ -912,12 +915,14 @@ void shouldGetRealDoneCardWhenCallGetStoryPointsAndCycleTimeWhenUseLastAssigneeF .thenReturn(CARD1_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD().build()); when(jiraFeignClient.getJiraCardHistory(baseUrl, "ADM-524", 0, 100, token)) .thenReturn(CARD2_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD().build()); + when(jiraFeignClient.getJiraCardHistory(baseUrl, "ADM-520", 0, 100, token)) + .thenReturn(CARD3_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); // then CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards(request, jiraBoardSetting.getBoardColumns(), List.of("da pei"), assigneeFilter); - assertThat(cardCollection.getCardsNumber()).isEqualTo(1); + assertThat(cardCollection.getCardsNumber()).isEqualTo(2); } @Test @@ -986,6 +991,8 @@ void shouldGetRealDoneCardsGivenMultipleStatuesMappingToDoneStatusWhenCallGetSto .thenReturn(CARD1_HISTORY_FOR_MULTIPLE_STATUSES().build()); when(jiraFeignClient.getJiraCardHistory(baseUrl, "ADM-524", 0, 100, token)) .thenReturn(CARD2_HISTORY_FOR_MULTIPLE_STATUSES().build()); + when(jiraFeignClient.getJiraCardHistory(baseUrl, "ADM-520", 0, 100, token)) + .thenReturn(CARD2_HISTORY_FOR_MULTIPLE_STATUSES().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); // then @@ -1023,6 +1030,8 @@ void shouldGetRealDoneCardsGivenHistoryWithNoStatusFieldWhenCallGetStoryPointsAn .thenReturn(CARD2_HISTORY_FOR_MULTIPLE_STATUSES().build()); when(jiraFeignClient.getJiraCardHistory(baseUrl, "ADM-524", 0, 100, token)) .thenReturn(CARD_HISTORY_WITH_NO_STATUS_FIELD().build()); + when(jiraFeignClient.getJiraCardHistory(baseUrl, "ADM-520", 0, 100, token)) + .thenReturn(CARD_HISTORY_WITH_NO_STATUS_FIELD().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); // then