From 7831851d577dc543fa7b819ddd5873d302f0e6c7 Mon Sep 17 00:00:00 2001 From: "weiran.sun" <907221539@qq.com> Date: Mon, 22 Jan 2024 15:33:40 +0800 Subject: [PATCH] ADM-729:[backend]refactor is real done card --- .../heartbeat/client/JiraFeignClient.java | 6 + .../service/board/jira/JiraService.java | 68 ++++------ .../jira/JiraBoardConfigDTOFixture.java | 119 ++++++++++++++++++ .../service/jira/JiraServiceTest.java | 9 +- 4 files changed, 159 insertions(+), 43 deletions(-) diff --git a/backend/src/main/java/heartbeat/client/JiraFeignClient.java b/backend/src/main/java/heartbeat/client/JiraFeignClient.java index d6e0876aa0..14bc228279 100644 --- a/backend/src/main/java/heartbeat/client/JiraFeignClient.java +++ b/backend/src/main/java/heartbeat/client/JiraFeignClient.java @@ -38,4 +38,10 @@ CardHistoryResponseDTO getJiraCardHistory(URI baseUrl, @PathVariable String jira @GetMapping(path = "/rest/api/2/issue/createmeta?projectKeys={projectKey}&expand=projects.issuetypes.fields") FieldResponseDTO getTargetField(URI baseUrl, @PathVariable String projectKey, @RequestHeader String authorization); + @GetMapping(path = "/rest/internal/2/issue/{jiraCardKey}/activityfeed?startAt={startAt}&maxResults={queryCount}") + @Cacheable(cacheNames = "jiraCardHistoryByCount", + key = "#jiraCardKey+'-'+#queryCount+'-'+#startAt+'-'+#authorization") + CardHistoryResponseDTO getJiraCardHistoryByCount(URI baseUrl, @PathVariable String jiraCardKey, + @PathVariable int startAt, @PathVariable int queryCount, @RequestHeader String authorization); + } 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 50bc79cc13..ca9a908ef9 100644 --- a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java +++ b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java @@ -486,7 +486,7 @@ private List getRealDoneCards(StoryPointsAndCycleTimeRequest reques log.info( "[Jira Service History] Successfully get jira card history, card key: {}, card history items size:{}", allDoneCard.getKey(), jiraCardHistory.getItems().size()); - if (isRealDoneCardByHistory(jiraCardHistory, request, allDoneCard.getKey())) { + if (isRealDoneCardByHistory(jiraCardHistory, request, allDoneCard)) { futures.add(allDoneCard); } else { @@ -571,52 +571,38 @@ private boolean useLastAssignee(String filterMethod) { } private boolean isRealDoneCardByHistory(CardHistoryResponseDTO jiraCardHistory, - StoryPointsAndCycleTimeRequest request, String card) { + StoryPointsAndCycleTimeRequest request, JiraCard allDoneCard) { List realDoneStatuses = request.getStatus().stream().map(String::toUpperCase).toList(); + long validStartTime = parseLong(request.getStartTime()); + long validEndTime = parseLong(request.getEndTime()); - List items = jiraCardHistory.getItems(); - List items2 = new ArrayList<>(); - List items3 = new ArrayList<>(); - for (HistoryDetail item : items) { - if (STATUS_FIELD_ID.equals(item.getFieldId())) { - items2.add(item); - } - } - if (items2.isEmpty()) { - log.error("[Jira Service Card RealDoneCards] Failed card history field id equal to status, card:{}", card); - } - for (HistoryDetail item : items2) { - if (realDoneStatuses.contains(item.getTo().getDisplayValue().toUpperCase())) { - items3.add(item); + Optional moveUndoneLastTime = jiraCardHistory.getItems() + .stream() + .filter(history -> STATUS_FIELD_ID.equals(history.getFieldId())) + .filter(history -> realDoneStatuses.contains(history.getFrom().getDisplayValue().toUpperCase())) + .filter(history -> !realDoneStatuses.contains(history.getTo().getDisplayValue().toUpperCase())) + .map(HistoryDetail::getTimestamp) + .filter(time -> time >= validStartTime && time <= validEndTime) + .max(Long::compareTo); + + Optional realDoneHistory = jiraCardHistory.getItems() + .stream() + .filter(history -> STATUS_FIELD_ID.equals(history.getFieldId())) + .filter(history -> !realDoneStatuses.contains(history.getFrom().getDisplayValue().toUpperCase())) + .filter(history -> realDoneStatuses.contains(history.getTo().getDisplayValue().toUpperCase())) + .filter(history -> history.getTimestamp() >= validStartTime && history.getTimestamp() <= validEndTime) + .filter(history -> history.getTimestamp() > moveUndoneLastTime.orElse(0L)) + .findFirst(); + + if (realDoneHistory.isPresent()) { + if (Objects.nonNull(allDoneCard.getFields().getStatus())) { + allDoneCard.getFields().getStatus().setName(realDoneHistory.get().getTo().getDisplayValue()); } - } - if (items3.isEmpty()) { - log.error( - "[Jira Service Card RealDoneCards] Failed to move card history to real done, card:{}, real done status: {}", - card, realDoneStatuses); + return true; } else { - log.info( - "[Jira Service Card RealDoneCards] Successfully to get history, card:{}, history{}, real done status: {}", - card, items3, realDoneStatuses); + return false; } - Optional lastTimeToRealDone = items3.stream().map(HistoryDetail::getTimestamp).max(Long::compareTo); - - long validStartTime = parseLong(request.getStartTime()); - long validEndTime = parseLong(request.getEndTime()); - return lastTimeToRealDone.filter(lastTime -> { - if (validStartTime <= lastTime && validEndTime >= lastTime) { - return true; - } - else { - log.error( - "[Jira Service Card RealDoneCards] Failed lastTime not in the valid time card:{} last time:{}, start time:{}, end time:{}", - card, new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH 时").format(new Date(lastTime)), - new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH 时").format(new Date(validStartTime)), - new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH 时").format(new Date(validEndTime))); - return false; - } - }).isPresent(); } private CycleTimeInfoDTO getCycleTime(CardHistoryResponseDTO cardHistoryResponseDTO, Boolean treatFlagCardAsBlock, diff --git a/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java b/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java index ac78f6acf7..44cdf5cc10 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java @@ -64,6 +64,10 @@ public class JiraBoardConfigDTOFixture { public static final String ASSIGNEE_NAME = "Zhang San"; + public static final String START_TIME = "1672556350000"; + + public static final String END_TIME = "1676908799000"; + public static JiraBoardConfigDTO.JiraBoardConfigDTOBuilder JIRA_BOARD_CONFIG_RESPONSE_BUILDER() { return JiraBoardConfigDTO.builder() @@ -228,6 +232,121 @@ public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_ new HistoryDetail(1686908799000L, "status", new Status("Done"), new Status(TESTING), null))); } + public static AllDoneCardsResponseDTO.AllDoneCardsResponseDTOBuilder ALL_REAL_DONE_CARDS_RESPONSE_FOR_STORY_POINT_BUILDER() { + return AllDoneCardsResponseDTO.builder() + .total("2") + .issues(List.of( + new JiraCard("1", + JiraCardField.builder() + .status(new Status()) + .assignee(new Assignee(ASSIGNEE_NAME)) + .storyPoints(2) + .build()), + new JiraCard("1", + JiraCardField.builder() + .status(new Status()) + .assignee(new Assignee(ASSIGNEE_NAME)) + .storyPoints(1) + .build()), + new JiraCard("1", + JiraCardField.builder() + .status(new Status()) + .assignee(new Assignee(ASSIGNEE_NAME)) + .storyPoints(3) + .build()), + new JiraCard("1", + JiraCardField.builder() + .status(new Status()) + .assignee(new Assignee(ASSIGNEE_NAME)) + .storyPoints(5) + .build()), + new JiraCard("2", + JiraCardField.builder() + .status(new Status()) + .assignee(new Assignee(ASSIGNEE_NAME)) + .storyPoints(5) + .build()))); + } + + public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_REAL_DONE_RESPONSE_BUILDER() { + return CardHistoryResponseDTO.builder() + .isLast(true) + .items(List.of(new HistoryDetail(1672556350002L, "status", new Status("In Dev"), new Status("To do"), null), + new HistoryDetail(1672556350003L, "status", new Status(REVIEW), new Status("In Dev"), null), + new HistoryDetail(1672556350004L, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), + null), + new HistoryDetail(1672556350005L, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), + null), + new HistoryDetail(1672556350006L, "status", new Status(BLOCK), new Status(TESTING), null), + new HistoryDetail(1672556350007L, "status", new Status(WAITING_FOR_TESTING), new Status(BLOCK), + null), + new HistoryDetail(1672556350008L, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), + null), + new HistoryDetail(1672556350010L, "status", new Status("Done"), new Status(TESTING), null))); + } + + public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_MULTI_REAL_DONE_RESPONSE_BUILDER() { + return CardHistoryResponseDTO.builder() + .isLast(true) + .items(List.of(new HistoryDetail(1672642730000L, "status", new Status("To do"), new Status(BLOCK), null), + new HistoryDetail(1672642730000L, "assignee", new Status("In Dev"), new Status("To do"), null), + new HistoryDetail(1672642730000L, "status", new Status(REVIEW), new Status("In Dev"), null), + new HistoryDetail(1672642730000L, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), + null), + new HistoryDetail(1672642740000L, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), + null), + new HistoryDetail(1672642740001L, "status", new Status(BLOCK), new Status(TESTING), null), + new HistoryDetail(1672642740002L, "status", new Status(FLAG), new Status(BLOCK), null), + new HistoryDetail(1672642750001L, "customfield_10021", new Status("Impediment"), new Status(FLAG), + null), + new HistoryDetail(1672642750002L, "flagged", new Status("Impediment"), new Status("removeFlag"), + null), + new HistoryDetail(1672642750003L, "status", new Status("Done"), new Status(TESTING), null), + new HistoryDetail(1672642750004L, "status", new Status("Done"), new Status(TESTING), null), + new HistoryDetail(1672642750005L, "customfield_10021", new Status(UNKNOWN), + new Status("removeFlag"), null))); + } + + public static JiraBoardSetting.JiraBoardSettingBuilder JIRA_BOARD_REAL_DONE_SETTING_BUILD() { + return JiraBoardSetting.builder() + .boardId(BOARD_ID) + .boardColumns(List.of(RequestJiraBoardColumnSetting.builder().name(IN_DEV).value(IN_DEV).build(), + RequestJiraBoardColumnSetting.builder().name(ANALYSE).value(ANALYSE).build(), + RequestJiraBoardColumnSetting.builder() + .name(WAITING_FOR_TESTING) + .value(WAITING_FOR_TESTING) + .build(), + RequestJiraBoardColumnSetting.builder().name(BLOCK).value(BLOCK).build(), + RequestJiraBoardColumnSetting.builder().name(TESTING).value(DONE).build(), + RequestJiraBoardColumnSetting.builder().name(REVIEW).value(REVIEW).build(), + RequestJiraBoardColumnSetting.builder().name(FLAG).value(FLAG).build(), + RequestJiraBoardColumnSetting.builder().name(UNKNOWN).value(UNKNOWN).build())) + .token("token") + .site("site") + .doneColumn(List.of(TESTING, "DONE")) + .treatFlagCardAsBlock(true) + .type("jira") + .projectKey("PLL") + .targetFields(List.of(TargetField.builder().key("testKey1").name("Story Points").flag(true).build(), + TargetField.builder().key("testKey2").name("Sprint").flag(true).build(), + TargetField.builder().key("testKey3").name("Flagged").flag(true).build())); + } + + public static StoryPointsAndCycleTimeRequest.StoryPointsAndCycleTimeRequestBuilder STORY_POINTS_FORM_ALL_REAL_DONE_CARD() { + JiraBoardSetting jiraBoardSetting = JIRA_BOARD_REAL_DONE_SETTING_BUILD().build(); + return StoryPointsAndCycleTimeRequest.builder() + .token("token") + .type(jiraBoardSetting.getType()) + .site(jiraBoardSetting.getSite()) + .project(jiraBoardSetting.getProjectKey()) + .boardId(jiraBoardSetting.getBoardId()) + .status(jiraBoardSetting.getDoneColumn()) + .startTime(START_TIME) + .endTime(END_TIME) + .targetFields(jiraBoardSetting.getTargetFields()) + .treatFlagCardAsBlock(jiraBoardSetting.getTreatFlagCardAsBlock()); + } + public static FieldResponseDTO.FieldResponseDTOBuilder FIELD_RESPONSE_BUILDER() { IssueField storyPointIssueField = new IssueField("customfield_10016", "Story point estimate"); IssueField sprintIssueField = new IssueField("customfield_10020", "Sprint"); diff --git a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java index 536bd09893..b6132e93dc 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java @@ -47,6 +47,7 @@ import static heartbeat.service.jira.JiraBoardConfigDTOFixture.ALL_DONE_TWO_PAGES_CARDS_RESPONSE_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.ALL_FIELD_RESPONSE_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.ALL_NON_DONE_CARDS_RESPONSE_FOR_STORY_POINT_BUILDER; +import static heartbeat.service.jira.JiraBoardConfigDTOFixture.ALL_REAL_DONE_CARDS_RESPONSE_FOR_STORY_POINT_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.BOARD_ID; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD1_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER_METHOD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD1_HISTORY_FOR_MULTIPLE_STATUSES; @@ -54,7 +55,9 @@ 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_REAL_DONE_RESPONSE_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_MULTI_RESPONSE_BUILDER; +import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_REAL_DONE_RESPONSE_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_RESPONSE_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_RESPONSE_BUILDER_TO_DONE; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_WITH_NO_STATUS_FIELD; @@ -73,6 +76,7 @@ import static heartbeat.service.jira.JiraBoardConfigDTOFixture.INCORRECT_JIRA_BOARD_SETTING_BUILD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.INCORRECT_JIRA_STORY_POINTS_FORM_ALL_DONE_CARD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.JIRA_BOARD_CONFIG_RESPONSE_BUILDER; +import static heartbeat.service.jira.JiraBoardConfigDTOFixture.JIRA_BOARD_REAL_DONE_SETTING_BUILD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.JIRA_BOARD_SETTING_BUILD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.JIRA_BOARD_SETTING_HAVE_UNKNOWN_COLUMN_BUILD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.JIRA_BOARD_SETTING_WITH_HISTORICAL_ASSIGNEE_FILTER_METHOD; @@ -81,6 +85,7 @@ import static heartbeat.service.jira.JiraBoardConfigDTOFixture.ONE_PAGE_NO_DONE_CARDS_RESPONSE_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.STORY_POINTS_FORM_ALL_DONE_CARD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.STORY_POINTS_FORM_ALL_DONE_CARD_WITH_EMPTY_STATUS; +import static heartbeat.service.jira.JiraBoardConfigDTOFixture.STORY_POINTS_FORM_ALL_REAL_DONE_CARD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.STORY_POINTS_REQUEST_WITH_ASSIGNEE_FILTER_METHOD; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.STORY_POINTS_REQUEST_WITH_MULTIPLE_REAL_DONE_STATUSES; import static org.assertj.core.api.Assertions.assertThat; @@ -600,7 +605,7 @@ void shouldGetCardsWhenCallGetStoryPointsAndCycleTimeWhenBoardTypeIsClassicJira( CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); - assertThat(cardCollection.getCardsNumber()).isEqualTo(1); + assertThat(cardCollection.getCardsNumber()).isEqualTo(0); } @Test @@ -627,7 +632,7 @@ void shouldGetCardsWhenCallGetStoryPointsAndCycleTimeWhenDoneTimeGreaterThanSele CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); - assertThat(cardCollection.getCardsNumber()).isEqualTo(1); + assertThat(cardCollection.getCardsNumber()).isEqualTo(0); } @Test