Skip to content

Commit

Permalink
ADM-822: [frontend] fix: fix pooling logic (#1102)
Browse files Browse the repository at this point in the history
* ADM-822:[backend]fix: process nullable board and dora metric ready status correctly

* Revert "ADM-822:[backend]fix: process nullable board and dora metric ready status correctly"

This reverts commit dd94e6d.

* ADM-822: [frontend] fix: fix pooling logic

* ADM-822: [frontend] fix: fix pooling logic

* ADM-822: [frontend] fix: fix test error

* ADM-822:[frontend]fix: fix test for doraMetricsCompleted and boardMetricsCompleted

* ADM-822: [frontend] fix: fix test error

* ADM-822: [frontend] fix: fix test error

* ADM-825 [frontend] fix: fix asynchronous issues

---------

Co-authored-by: Yunlong Gan <[email protected]>
Co-authored-by: Tingyu Dong <[email protected]>
Co-authored-by: xuebing <[email protected]>
  • Loading branch information
4 people authored Feb 29, 2024
1 parent 9775d41 commit a2a3a9d
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
import heartbeat.handler.base.AsyncDataBaseHandler;
import heartbeat.service.report.MetricsDataDTO;
import heartbeat.util.IdUtil;
import heartbeat.util.ValueUtil;
import jakarta.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.Objects;
import java.util.stream.Stream;

import static heartbeat.handler.base.FIleType.METRICS_DATA_COMPLETED;

Expand Down Expand Up @@ -56,19 +60,30 @@ public void updateMetricsDataCompletedInHandler(String metricDataFileId, MetricT
}

public MetricsDataDTO getReportReadyStatusByTimeStamp(String timeStamp) {
boolean isBoardReady = getReadyStatus(IdUtil.getBoardReportId(timeStamp), MetricType.BOARD);
boolean isDoraReady = getReadyStatus(IdUtil.getDoraReportId(timeStamp), MetricType.DORA);
return new MetricsDataDTO(isBoardReady, isDoraReady, isBoardReady && isDoraReady);
Boolean boardReadyStatus = getReadyStatus(IdUtil.getBoardReportId(timeStamp), MetricType.BOARD);
boolean isBoardReady = ValueUtil.valueOrDefault(false, boardReadyStatus);

Boolean doraReadyStatus = getReadyStatus(IdUtil.getDoraReportId(timeStamp), MetricType.DORA);
boolean isDoraReady = ValueUtil.valueOrDefault(false, doraReadyStatus);

boolean isReportReady = Stream.of(boardReadyStatus, doraReadyStatus)
.filter(Objects::nonNull)
.allMatch(Boolean::booleanValue);
return new MetricsDataDTO(isBoardReady, isDoraReady, isReportReady);
}

private boolean getReadyStatus(String fileId, MetricType metricType) {
@Nullable
private Boolean getReadyStatus(String fileId, MetricType metricType) {
MetricsDataCompleted metricsDataCompleted = getMetricsDataCompleted(fileId);
if (metricsDataCompleted == null) {
return false;
return null;
}
else if (metricType == MetricType.BOARD) {
return metricsDataCompleted.boardMetricsCompleted();
}
else {
return metricsDataCompleted.doraMetricsCompleted();
}

return metricType == MetricType.BOARD ? metricsDataCompleted.boardMetricsCompleted()
: metricsDataCompleted.doraMetricsCompleted();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,15 @@ void shouldUpdateDoraMetricDataWhenPreviousMetricsStatusIsNotNullAndMetricTypeIs
class GetReportReadyStatusByTimeStamp {

@Test
void shouldGetReadyFalseAndAllMetricsReadyFalseGivenPreviousMetricsStatusIsNull() throws IOException {
void shouldGetReadyFalseAndAllMetricsReadyTrueGivenPreviousMetricsStatusIsNull() throws IOException {
long currentTimeMillis = System.currentTimeMillis();
String currentTime = Long.toString(currentTimeMillis);

MetricsDataDTO result = asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(currentTime);

assertEquals(false, result.isBoardReady());
assertEquals(false, result.isDoraReady());
assertEquals(false, result.isAllMetricsReady());
assertEquals(true, result.isAllMetricsReady());
Files.deleteIfExists(Path.of(APP_OUTPUT_METRICS + "/" + currentTime));
assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(currentTime));
}
Expand Down
1 change: 1 addition & 0 deletions frontend/__tests__/containers/ReportButtonGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe('test', () => {
startDate={''}
endDate={''}
csvTimeStamp={1239013}
allDataCompleted={true}
/>
</Provider>,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jest.mock('@src/hooks/useGenerateReportEffect', () => ({
stopPollingReports: jest.fn(),
isServerError: false,
errorMessage: '',
allDataCompleted: true,
}),
}));

Expand Down
27 changes: 25 additions & 2 deletions frontend/__tests__/hooks/useGenerateReportEffect.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ describe('use generate report effect', () => {
});
});

it('should call polling report more than one time when allMetricsReady field in response is false', async () => {
it('should call polling report more than one time when boardMetricsCompleted field in response is false given call boardData ', async () => {
reportClient.polling = jest.fn().mockImplementation(async () => ({
status: HttpStatusCode.NoContent,
response: { ...MOCK_REPORT_RESPONSE, allMetricsCompleted: false },
response: { ...MOCK_REPORT_RESPONSE, boardMetricsCompleted: false },
}));
reportClient.retrieveByUrl = jest
.fn()
Expand All @@ -75,6 +75,29 @@ describe('use generate report effect', () => {
});
});

it('should call polling report more than one time when doraMetricsCompleted field in response is false given call doraData ', async () => {
reportClient.polling = jest.fn().mockImplementation(async () => ({
status: HttpStatusCode.NoContent,
response: { ...MOCK_REPORT_RESPONSE, doraMetricsCompleted: false },
}));
reportClient.retrieveByUrl = jest
.fn()
.mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE }));

const { result } = renderHook(() => useGenerateReportEffect());

await waitFor(() => {
result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS);
});
act(() => {
jest.advanceTimersByTime(10000);
});

await waitFor(() => {
expect(reportClient.polling).toHaveBeenCalledTimes(2);
});
});

it('should call polling report only once when calling startToRequestBoardData but startToRequestDoraData called before', async () => {
reportClient.polling = jest
.fn()
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/containers/ReportButtonGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface ReportButtonGroupProps {
isShowExportBoardButton: boolean;
isShowExportPipelineButton: boolean;
isShowExportMetrics: boolean;
allDataCompleted: boolean;
}

export const ReportButtonGroup = ({
Expand All @@ -34,6 +35,7 @@ export const ReportButtonGroup = ({
isShowExportMetrics,
isShowExportBoardButton,
isShowExportPipelineButton,
allDataCompleted,
}: ReportButtonGroupProps) => {
const { fetchExportData, isExpired } = useExportCsvEffect();

Expand Down Expand Up @@ -75,7 +77,7 @@ export const ReportButtonGroup = ({
</BackButton>
{isShowExportMetrics && (
<StyledExportButton
disabled={!(reportData?.allMetricsCompleted && !isReportHasError)}
disabled={!(allDataCompleted && !isReportHasError)}
onClick={() => handleDownload(REPORT_TYPES.METRICS, startDate, endDate)}
>
{COMMON_BUTTONS.EXPORT_METRIC_DATA}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/containers/ReportStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const ReportStep = ({ handleSave }: ReportStepProps) => {
generalError4Board,
generalError4Dora,
generalError4Report,
allDataCompleted,
} = useGenerateReportEffect();

const [exportValidityTimeMin, setExportValidityTimeMin] = useState<number | undefined | null>(undefined);
Expand Down Expand Up @@ -289,6 +290,7 @@ const ReportStep = ({ handleSave }: ReportStepProps) => {
startDate={startDate}
endDate={endDate}
csvTimeStamp={csvTimeStamp}
allDataCompleted={allDataCompleted}
/>
</>
);
Expand Down
35 changes: 33 additions & 2 deletions frontend/src/hooks/useGenerateReportEffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface useGenerateReportEffectInterface {
generalError4Dora: string;
generalError4Report: string;
reportData: ReportResponseDTO | undefined;
allDataCompleted: boolean;
}

export const useGenerateReportEffect = (): useGenerateReportEffectInterface => {
Expand All @@ -28,15 +29,19 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => {
const [generalError4Board, setGeneralError4Board] = useState('');
const [generalError4Dora, setGeneralError4Dora] = useState('');
const [generalError4Report, setGeneralError4Report] = useState('');
const [allDataCompleted, setAllDataCompleted] = useState(false);
const [reportData, setReportData] = useState<ReportResponseDTO | undefined>();
const timerIdRef = useRef<number>();
const timerIdRef = useRef<number | undefined>();
let hasPollingStarted = false;
const doraCalled = useRef<boolean>(false);
const boardCalled = useRef<boolean>(false);

const startToRequestBoardData = (boardParams: ReportRequestDTO) => {
setTimeout4Board('');
reportClient
.retrieveByUrl(boardParams, `${reportPath}/${METRIC_TYPES.BOARD}`)
.then((res) => {
boardCalled.current = true;
if (hasPollingStarted) return;
hasPollingStarted = true;
pollingReport(res.response.callbackUrl, res.response.interval);
Expand Down Expand Up @@ -71,6 +76,7 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => {
reportClient
.retrieveByUrl(doraParams, `${reportPath}/${METRIC_TYPES.DORA}`)
.then((res) => {
doraCalled.current = true;
if (hasPollingStarted) return;
hasPollingStarted = true;
pollingReport(res.response.callbackUrl, res.response.interval);
Expand All @@ -80,14 +86,38 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => {
});
};

const setAllMetricsCompleted = (response: ReportResponseDTO) => {
if (doraCalled.current && boardCalled.current) {
setAllDataCompleted(response.boardMetricsCompleted && response.doraMetricsCompleted);
} else if (doraCalled.current && !boardCalled.current) {
setAllDataCompleted(response.doraMetricsCompleted);
} else if (!doraCalled.current && boardCalled.current) {
setAllDataCompleted(response.boardMetricsCompleted);
}
};

const checkAllMetricsCompleted = (boardMetricsCompleted: boolean, doraMetricsCompleted: boolean) => {
if (doraCalled.current && boardCalled.current) {
return boardMetricsCompleted && doraMetricsCompleted;
} else if (doraCalled.current && !boardCalled.current) {
return doraMetricsCompleted;
} else if (!doraCalled.current && boardCalled.current) {
return boardMetricsCompleted;
}
};

const pollingReport = (url: string, interval: number) => {
setTimeout4Report('');
reportClient
.polling(url)
.then((res: { status: number; response: ReportResponseDTO }) => {
const response = res.response;
handleAndUpdateData(response);
if (response.allMetricsCompleted || !hasPollingStarted) {
setAllMetricsCompleted(response);
if (
checkAllMetricsCompleted(response.boardMetricsCompleted, response.doraMetricsCompleted) ||
!hasPollingStarted
) {
stopPollingReports();
} else {
timerIdRef.current = window.setTimeout(() => pollingReport(url, interval), interval * 1000);
Expand Down Expand Up @@ -120,5 +150,6 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => {
generalError4Board,
generalError4Dora,
generalError4Report,
allDataCompleted,
};
};

0 comments on commit a2a3a9d

Please sign in to comment.