Skip to content

Commit

Permalink
ADM-999 [frontend][docs]: update readme and fix sonar issue (#1596)
Browse files Browse the repository at this point in the history
* ADM-999 [docs]: update readme

* ADM-999 [frontend]: fix sonar issue

* ADM-999 [frontend]: change the loading logic

* ADM-999 [frontend]: fix e2e
  • Loading branch information
zhou-yinyuan authored Sep 4, 2024
1 parent 389977a commit c8cc1a2
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 46 deletions.
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ According to your selected required data, you need to input account settings for
| Pipeline change failure rate | Pipeline |
| Pipeline mean time to recovery | Pipeline |

If only `Lead time for changes` is selected among the four DORA metrics - `Lead time for changes`, `Deployment frequency`, `Pipeline change failure rate`, and `Pipeline mean time to recovery`, you will see another option `None` in the pipeline tool configuration. If you choose the `None` option, when calculating `Lead time for changes`, only the `PR lead time` will be considered, and `pipeline lead time` will not be calculated.
If only `Lead time for changes` is selected among the four DORA metrics - `Lead time for changes`, `Deployment frequency`, `Pipeline change failure rate`, and `Pipeline mean time to recovery`, you will see another option `Other` in the pipeline tool configuration. If you choose the `Other` option, when calculating `Lead time for changes`, only the `PR lead time` will be considered, and `pipeline lead time` will not be calculated.

![Image 3-4](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/3-4-1.png)\
Image 3-4,Project config
Expand All @@ -200,10 +200,10 @@ _Image 3-5, create Jira token_

**The details for Pipeline:**

|Items| Description |
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|PipelineTool| The pipeline tool you team use, currently heartbeat only support buildkite. If only `Lead time for changes` is selected among the four DORA metrics, the `None` option will appear, indicating that when calculating `Lead time for changes`, only `PR lead time` will be considered, and `pipeline lead time` will not be calculated. |
|Token| Generate buildkite token with below link, https://buildkite.com/user/api-access-tokens |
| Items | Description |
|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| PipelineTool | The pipeline tool you team use, currently heartbeat only support buildkite. If only `Lead time for changes` is selected among the four DORA metrics, the `Other` option will appear, indicating that when calculating `Lead time for changes`, only `PR lead time` will be considered, and `pipeline lead time` will not be calculated. |
| Token | Generate buildkite token with below link, https://buildkite.com/user/api-access-tokens |

![Image 3-5](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/select-none-option-in-the-pipeline-configuration.png)

Expand Down Expand Up @@ -329,6 +329,21 @@ The data source of **crew setting** is Github code committer. Heartbeat will lis

If builds were manually/scheduled triggered or could not find code committer from Github, Heartbeat will mark as "Unknown" in crew setting.


#### 3.2.6 Source Control configuration

![Image 3-19](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/source-control-configuration-in-metrics-page.png)\
_Image 3-19,Settings for Source control_

They are sharing the similar settings to pipeline configuration

| Items | Description |
|--------------|------------------------------------------|
| Organization | The organization for your source control |
| Repo | The repo for your source control |
| Branches | Your selected branches |
| Crew setting | Your selected author from github |

## 3.3 Export and import config info

### 3.3.1 Export Config Json File
Expand Down
35 changes: 35 additions & 0 deletions frontend/__tests__/context/configSlice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,41 @@ describe('config reducer', () => {
expect(config.sourceControl.verifiedResponse.repoList.children).toEqual(expectedSourceControlVerifiedRepoList1);
expect(config2.sourceControl.verifiedResponse.repoList.children).toEqual(expectedSourceControlVerifiedRepoList2);
});

it('should clear source control verified repo list', () => {
const initialState = {
...initialConfigState,
sourceControl: {
...initialConfigState.sourceControl,
verifiedResponse: {
repoList: {
children: [
{
name: 'organization',
value: 'mock-org1',
children: [],
},
{
name: 'organization',
value: 'mock-org2',
children: [],
},
],
name: 'root',
value: '-1',
},
},
},
};
const expectedSourceControlVerifiedRepoList: string[] = [];
const action = {
type: 'config/clearSourceControlVerifiedResponse',
};

const config = configReducer(initialState, action);

expect(config.sourceControl.verifiedResponse.repoList.children).toEqual(expectedSourceControlVerifiedRepoList);
});
});

describe('select methods', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jest.mock('react-redux', () => ({
useDispatch: () => mockDispatch,
useSelector: (selector: <TSelected>() => TSelected) => {
const originalUseSelector = jest.requireActual('react-redux').useSelector;
if (selector.name === 'selectShouldGetSourceControlConfig') {
return true;
}
return originalUseSelector(selector);
},
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ jest.mock('react-redux', () => ({
useDispatch: () => mockDispatch,
useSelector: (selector: <TSelected>() => TSelected) => {
const originalUseSelector = jest.requireActual('react-redux').useSelector;
if (selector.name === 'selectShouldGetSourceControlConfig') {
return true;
}
return originalUseSelector(selector);
},
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ jest.mock('react-redux', () => ({
useDispatch: () => mockDispatch,
useSelector: (selector: <TSelected>() => TSelected) => {
const originalUseSelector = jest.requireActual('react-redux').useSelector;
if (selector.name === 'selectShouldGetSourceControlConfig') {
return true;
}
return originalUseSelector(selector);
},
}));
Expand Down
10 changes: 5 additions & 5 deletions frontend/e2e/fixtures/import-file/chart-step-data.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const chartStepData = {
unSelectBranch: 'main',
addNewBranch: ['ADM-963'],
addNewBranch: ['ADM-998'],
errorDateRange: [
{
startDate: '2024-09-07T00:00:00.000+08:00',
Expand All @@ -19,12 +19,12 @@ export const chartStepData = {
],
rightDateRange: [
{
startDate: '2024-06-03T00:00:00.000+08:00',
endDate: '2024-06-06T23:59:59.999+08:00',
startDate: '2024-08-12T00:00:00.000+08:00',
endDate: '2024-08-25T23:59:59.999+08:00',
},
{
startDate: '2024-06-07T00:00:00.000+08:00',
endDate: '2024-06-07T23:59:59.999+08:00',
startDate: '2024-08-26T00:00:00.000+08:00',
endDate: '2024-09-02T23:59:59.999+08:00',
},
],
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"projectName": "Heartbeat Metrics",
"dateRange": {
"startDate": "2024-06-03T00:00:00.000+08:00",
"endDate": "2024-06-07T23:59:59.999+08:00"
"startDate": "2024-08-12T00:00:00.000+08:00",
"endDate": "2024-09-02T23:59:59.999+08:00"
},
"calendarType": "CN",
"metrics": [
Expand Down
2 changes: 1 addition & 1 deletion frontend/e2e/pages/metrics/metrics-step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ export class MetricsStep {
await this.pipelineBranchSelect.click();
for (const branchName of branches) {
await this.page.getByRole('combobox', { name: 'Branches' }).fill(branchName);
await this.page.getByRole('option', { name: branchName }).getByRole('checkbox').check();
await this.page.getByRole('option', { name: branchName }).getByRole('checkbox').first().check();
}
await this.page.keyboard.press('Escape');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ import {
selectSourceControlBranches,
selectDateRange,
} from '@src/context/config/configSlice';
import {
selectSourceControlConfigurationSettings,
updateShouldGetSourceControlConfig,
} from '@src/context/Metrics/metricsSlice';
import { useGetSourceControlConfigurationBranchEffect } from '@src/hooks/useGetSourceControlConfigurationBranchEffect';
import { useGetSourceControlConfigurationRepoEffect } from '@src/hooks/useGetSourceControlConfigurationRepoEffect';
import { useGetSourceControlConfigurationCrewEffect } from '@src/hooks/useGetSourceControlConfigurationCrewEffect';
import { SourceControlBranch } from '@src/containers/MetricsStep/SouceControlConfiguration/SourceControlBranch';
import { SingleSelection } from '@src/containers/MetricsStep/DeploymentFrequencySettings/SingleSelection';
import { selectSourceControlConfigurationSettings } from '@src/context/Metrics/metricsSlice';
import { useAppDispatch, useAppSelector } from '@src/hooks';
import { Loading } from '@src/components/Loading';
import { useAppSelector } from '@src/hooks';
import { store } from '@src/store';
import { useEffect } from 'react';

Expand Down Expand Up @@ -62,6 +65,7 @@ export const SourceControlMetricSelection = ({
isGetAllCrews,
} = useGetSourceControlConfigurationCrewEffect();
const storeContext = store.getState();
const dispatch = useAppDispatch();
const organizationNameOptions = selectSourceControlOrganizations(storeContext);
const repoNameOptions = selectSourceControlRepos(storeContext, organization);
const branchNameOptions = selectSourceControlBranches(storeContext, organization, repo);
Expand Down Expand Up @@ -98,10 +102,15 @@ export const SourceControlMetricSelection = ({

useEffect(() => {
if (!isGetAllCrews && organization && repo && selectedBranches) {
selectedBranches.forEach((it) => getSourceControlCrewInfo(organization, repo, it, dateRanges));
Promise.all(selectedBranches.map((it) => getSourceControlCrewInfo(organization, repo, it, dateRanges))).then(
() => {
dispatch(updateShouldGetSourceControlConfig(false));
},
);
}
}, [
dateRanges,
dispatch,
getSourceControlBranchInfo,
getSourceControlCrewInfo,
getSourceControlRepoInfo,
Expand All @@ -124,7 +133,11 @@ export const SourceControlMetricSelection = ({
const handleOnUpdateBranches = (id: number, label: string, value: string[]): void => {
const branchNeedGetCrews = value.filter((it) => selectedBranches?.every((branch) => branch !== it));
onUpdateSourceControl(id, label, value);
branchNeedGetCrews.forEach((branch) => getSourceControlCrewInfo(organization, repo, branch, dateRanges));
Promise.all(
branchNeedGetCrews.map((branch) => getSourceControlCrewInfo(organization, repo, branch, dateRanges)),
).then(() => {
dispatch(updateShouldGetSourceControlConfig(false));
});
};

useEffect(() => {
Expand Down
41 changes: 22 additions & 19 deletions frontend/src/context/Metrics/metricsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,26 +403,25 @@ export const metricsSlice = createSlice({
state.sourceControlConfigurationSettings = sourceControlConfigurationSettings.map((it) => {
if (it.id !== updateId) {
return it;
}
if (label === 'organization') {
return {
...it,
organization: value,
repo: '',
branches: [],
};
} else if (label === 'repo') {
return {
...it,
repo: value,
branches: [],
};
} else {
if (label === 'organization') {
return {
...it,
organization: value,
repo: '',
branches: [],
};
} else if (label === 'repo') {
return {
...it,
repo: value,
branches: [],
};
} else {
return {
...it,
branches: value,
};
}
return {
...it,
branches: value,
};
}
});
},
Expand Down Expand Up @@ -494,6 +493,9 @@ export const metricsSlice = createSlice({
updateShouldGetPipelineConfig: (state, action) => {
state.shouldGetPipeLineConfig = action.payload;
},
updateShouldGetSourceControlConfig: (state, action) => {
state.shouldGetSourceControlConfig = action.payload;
},

updateMetricsImportedData: (state, action) => {
const {
Expand Down Expand Up @@ -839,6 +841,7 @@ export const {
updateAdvancedSettings,
updateShouldGetBoardConfig,
updateShouldGetPipelineConfig,
updateShouldGetSourceControlConfig,
updateReworkTimesSettings,
updateFirstTimeRoadMetricsBoardData,
updateShouldRetryPipelineConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {
selectShouldGetSourceControlConfig,
updateSourceControlConfigurationSettingsFirstInto,
} from '@src/context/Metrics/metricsSlice';
import { selectSourceControl, updateSourceControlVerifiedResponse } from '@src/context/config/configSlice';
import { updateSourceControlConfigurationSettingsFirstInto } from '@src/context/Metrics/metricsSlice';
import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient';
import { useAppDispatch, useAppSelector } from '@src/hooks/index';
import { SourceControlTypes } from '@src/constants/resources';
Expand All @@ -14,7 +17,8 @@ export interface IUseGetSourceControlConfigurationBranchInterface {
export const useGetSourceControlConfigurationBranchEffect = (): IUseGetSourceControlConfigurationBranchInterface => {
const dispatch = useAppDispatch();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isGetBranch, setIsGetBranch] = useState<boolean>(false);
const shouldGetSourceControlConfig = useAppSelector(selectShouldGetSourceControlConfig);
const [isGetBranch, setIsGetBranch] = useState<boolean>(!shouldGetSourceControlConfig);
const restoredSourceControlInfo = useAppSelector(selectSourceControl);

function getEnumKeyByEnumValue(enumValue: string): SourceControlTypes {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DateRange, selectSourceControl, updateSourceControlVerifiedResponse } from '@src/context/config/configSlice';
import { selectShouldGetSourceControlConfig } from '@src/context/Metrics/metricsSlice';
import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient';
import { FULFILLED, SourceControlTypes } from '@src/constants/resources';
import { useAppDispatch, useAppSelector } from '@src/hooks/index';
Expand All @@ -18,7 +19,8 @@ export interface IUseGetSourceControlConfigurationCrewInterface {
export const useGetSourceControlConfigurationCrewEffect = (): IUseGetSourceControlConfigurationCrewInterface => {
const dispatch = useAppDispatch();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isGetAllCrews, setIsGetAllCrews] = useState<boolean>(false);
const shouldGetSourceControlConfig = useAppSelector(selectShouldGetSourceControlConfig);
const [isGetAllCrews, setIsGetAllCrews] = useState<boolean>(!shouldGetSourceControlConfig);
const restoredSourceControlInfo = useAppSelector(selectSourceControl);

function getEnumKeyByEnumValue(enumValue: string): SourceControlTypes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,17 @@ export const useGetSourceControlConfigurationOrganizationEffect =
}, [dispatch, restoredSourceControlInfo.token, restoredSourceControlInfo.type]);

useEffect(() => {
if (!apiTouchedRef.current && !isLoading) {
if (!apiTouchedRef.current && !isLoading && shouldGetSourceControlConfig) {
apiTouchedRef.current = true;
getSourceControlInfo();
}
}, [getSourceControlInfo, isLoading]);
}, [getSourceControlInfo, isLoading, shouldGetSourceControlConfig]);

useEffect(() => {
dispatch(clearSourceControlVerifiedResponse());
}, [dispatch, restoredSourceControlInfo.token]);
if (shouldGetSourceControlConfig) {
dispatch(clearSourceControlVerifiedResponse());
}
}, [dispatch, restoredSourceControlInfo.token, shouldGetSourceControlConfig]);

return {
isLoading,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {
selectShouldGetSourceControlConfig,
updateSourceControlConfigurationSettingsFirstInto,
} from '@src/context/Metrics/metricsSlice';
import { DateRange, selectSourceControl, updateSourceControlVerifiedResponse } from '@src/context/config/configSlice';
import { updateSourceControlConfigurationSettingsFirstInto } from '@src/context/Metrics/metricsSlice';
import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient';
import { FULFILLED, SourceControlTypes } from '@src/constants/resources';
import { useAppDispatch, useAppSelector } from '@src/hooks/index';
Expand All @@ -14,7 +17,8 @@ export interface IUseGetSourceControlConfigurationRepoInterface {
export const useGetSourceControlConfigurationRepoEffect = (): IUseGetSourceControlConfigurationRepoInterface => {
const dispatch = useAppDispatch();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isGetRepo, setIsGetRepo] = useState<boolean>(false);
const shouldGetSourceControlConfig = useAppSelector(selectShouldGetSourceControlConfig);
const [isGetRepo, setIsGetRepo] = useState<boolean>(!shouldGetSourceControlConfig);
const restoredSourceControlInfo = useAppSelector(selectSourceControl);

function getEnumKeyByEnumValue(enumValue: string): SourceControlTypes {
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/hooks/useVerifySourceControlTokenEffect.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { initDeploymentFrequencySettings, updateShouldGetPipelineConfig } from '@src/context/Metrics/metricsSlice';
import {
initDeploymentFrequencySettings,
updateShouldGetPipelineConfig,
updateShouldGetSourceControlConfig,
} from '@src/context/Metrics/metricsSlice';
import { SOURCE_CONTROL_ERROR_MESSAGE } from '@src/containers/ConfigStep/Form/literal';
import { SourceControlVerifyRequestDTO } from '@src/clients/sourceControl/dto/request';
import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient';
Expand Down Expand Up @@ -34,6 +38,7 @@ export const useVerifySourceControlTokenEffect = () => {
const persistReduxData = (sourceControlConfig: ISourceControlData) => {
dispatch(updateSourceControl(sourceControlConfig));
dispatch(updateShouldGetPipelineConfig(true));
dispatch(updateShouldGetSourceControlConfig(true));
dispatch(initDeploymentFrequencySettings());
};
const resetFields = () => {
Expand Down

0 comments on commit c8cc1a2

Please sign in to comment.