Use non-blocking way of fetching ruler api availability (#52102)

Co-authored-by: Joe Blubaugh <joe.blubaugh@grafana.com>
This commit is contained in:
Konrad Lalik 2022-07-13 11:50:20 +02:00 committed by GitHub
parent 21632817c5
commit 169a1c5b0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 39 deletions

View File

@ -3910,9 +3910,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
],
"public/app/features/alerting/unified/components/rules/RulesGroup.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/alerting/unified/components/silences/SilencesEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],

View File

@ -23,13 +23,14 @@ interface Props {
export const CloudRules: FC<Props> = ({ namespaces, expandAll }) => {
const styles = useStyles2(getStyles);
const dsConfigs = useUnifiedAlertingSelector((state) => state.dataSources);
const rules = useUnifiedAlertingSelector((state) => state.promRules);
const rulesDataSources = useMemo(getRulesDataSources, []);
const groupsWithNamespaces = useCombinedGroupNamespace(namespaces);
const dataSourcesLoading = useMemo(
() => rulesDataSources.filter((ds) => rules[ds.name]?.loading),
[rules, rulesDataSources]
() => rulesDataSources.filter((ds) => rules[ds.name]?.loading || dsConfigs[ds.name]?.loading),
[rules, dsConfigs, rulesDataSources]
);
const { numberOfPages, onPageChange, page, pageItems } = usePagination(

View File

@ -8,17 +8,26 @@ import { contextSrv } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
import { CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting';
import { useHasRuler } from '../../hooks/useHasRuler';
import { disableRBAC, mockCombinedRule, mockDataSource } from '../../mocks';
import { RulesGroup } from './RulesGroup';
const hasRulerMock = jest.fn<boolean, any>();
jest.mock('../../hooks/useHasRuler', () => ({
useHasRuler: () => hasRulerMock,
}));
jest.mock('../../hooks/useHasRuler');
const mocks = {
useHasRuler: jest.mocked(useHasRuler),
};
function mockUseHasRuler(hasRuler: boolean, rulerRulesLoaded: boolean) {
mocks.useHasRuler.mockReturnValue({
hasRuler: () => hasRuler,
rulerRulesLoaded: () => rulerRulesLoaded,
});
}
beforeEach(() => {
hasRulerMock.mockReset();
mocks.useHasRuler.mockReset();
});
const ui = {
@ -55,6 +64,7 @@ describe('Rules group tests', () => {
it('Should hide delete and edit group buttons', () => {
// Act
mockUseHasRuler(true, true);
renderRulesGroup(namespace, group);
// Assert
@ -83,33 +93,33 @@ describe('Rules group tests', () => {
it('When ruler enabled should display delete and edit group buttons', () => {
// Arrange
hasRulerMock.mockReturnValue(true);
mockUseHasRuler(true, true);
// Act
renderRulesGroup(namespace, group);
// Assert
expect(hasRulerMock).toHaveBeenCalled();
expect(mocks.useHasRuler).toHaveBeenCalled();
expect(ui.deleteGroupButton.get()).toBeInTheDocument();
expect(ui.editGroupButton.get()).toBeInTheDocument();
});
it('When ruler disabled should hide delete and edit group buttons', () => {
// Arrange
hasRulerMock.mockReturnValue(false);
mockUseHasRuler(false, false);
// Act
renderRulesGroup(namespace, group);
// Assert
expect(hasRulerMock).toHaveBeenCalled();
expect(mocks.useHasRuler).toHaveBeenCalled();
expect(ui.deleteGroupButton.query()).not.toBeInTheDocument();
expect(ui.editGroupButton.query()).not.toBeInTheDocument();
});
it('Delete button click should display confirmation modal', async () => {
// Arrange
hasRulerMock.mockReturnValue(true);
mockUseHasRuler(true, true);
// Act
renderRulesGroup(namespace, group);

View File

@ -43,13 +43,14 @@ export const RulesGroup: FC<Props> = React.memo(({ group, namespace, expandAll }
setIsCollapsed(!expandAll);
}, [expandAll]);
const hasRuler = useHasRuler();
const { hasRuler, rulerRulesLoaded } = useHasRuler();
const rulerRule = group.rules[0]?.rulerRule;
const folderUID = (rulerRule && isGrafanaRulerRule(rulerRule) && rulerRule.grafana_alert.namespace_uid) || undefined;
const { folder } = useFolder(folderUID);
// group "is deleting" if rules source has ruler, but this group has no rules that are in ruler
const isDeleting = hasRuler(rulesSource) && !group.rules.find((rule) => !!rule.rulerRule);
const isDeleting =
hasRuler(rulesSource) && rulerRulesLoaded(rulesSource) && !group.rules.find((rule) => !!rule.rulerRule);
const isFederated = isFederatedRuleGroup(group);
const deleteGroup = () => {

View File

@ -106,7 +106,7 @@ export const getStyles = (theme: GrafanaTheme2) => ({
});
function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean) {
const hasRuler = useHasRuler();
const { hasRuler, rulerRulesLoaded } = useHasRuler();
return useMemo((): RuleTableColumnProps[] => {
const columns: RuleTableColumnProps[] = [
@ -118,8 +118,8 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean) {
const { namespace } = rule;
const { rulesSource } = namespace;
const { promRule, rulerRule } = rule;
const isDeleting = !!(hasRuler(rulesSource) && promRule && !rulerRule);
const isCreating = !!(hasRuler(rulesSource) && rulerRule && !promRule);
const isDeleting = !!(hasRuler(rulesSource) && rulerRulesLoaded(rulesSource) && promRule && !rulerRule);
const isCreating = !!(hasRuler(rulesSource) && rulerRulesLoaded(rulesSource) && rulerRule && !promRule);
return <RuleState rule={rule} isDeleting={isDeleting} isCreating={isCreating} />;
},
size: '165px',
@ -188,5 +188,5 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean) {
});
}
return columns;
}, [hasRuler, showSummaryColumn, showGroupColumn]);
}, [hasRuler, rulerRulesLoaded, showSummaryColumn, showGroupColumn]);
}

View File

@ -2,18 +2,31 @@ import { useCallback } from 'react';
import { RulesSource } from 'app/types/unified-alerting';
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
import { getRulesSourceName, GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
import { useUnifiedAlertingSelector } from './useUnifiedAlertingSelector';
// datasource has ruler if it's grafana managed or if we're able to load rules from it
export function useHasRuler(): (rulesSource: string | RulesSource) => boolean {
export function useHasRuler() {
const rulerRules = useUnifiedAlertingSelector((state) => state.rulerRules);
return useCallback(
const hasRuler = useCallback(
(rulesSource: string | RulesSource) => {
const rulesSourceName = typeof rulesSource === 'string' ? rulesSource : rulesSource.name;
return rulesSourceName === GRAFANA_RULES_SOURCE_NAME || !!rulerRules[rulesSourceName]?.result;
},
[rulerRules]
);
const rulerRulesLoaded = useCallback(
(rulesSource: RulesSource) => {
const rulesSourceName = getRulesSourceName(rulesSource);
const result = rulerRules[rulesSourceName]?.result;
return Boolean(result);
},
[rulerRules]
);
return { hasRuler, rulerRulesLoaded };
}

View File

@ -255,30 +255,34 @@ export const fetchRulesSourceBuildInfoAction = createAsyncThunk(
const dataSources: AsyncRequestMapSlice<PromBasedDataSource> = (getState() as StoreState).unifiedAlerting
.dataSources;
const hasLoaded = Boolean(dataSources[rulesSourceName]?.result);
return !hasLoaded;
const hasError = Boolean(dataSources[rulesSourceName]?.error);
return !(hasLoaded || hasError);
},
}
);
export function fetchAllPromAndRulerRulesAction(force = false): ThunkResult<void> {
return async (dispatch, getStore) => {
await dispatch(fetchAllPromBuildInfoAction());
return Promise.all(
getAllRulesSourceNames().map(async (rulesSourceName) => {
await dispatch(fetchRulesSourceBuildInfoAction({ rulesSourceName }));
const { promRules, rulerRules, dataSources } = getStore().unifiedAlerting;
const { promRules, rulerRules, dataSources } = getStore().unifiedAlerting;
const dataSourceConfig = dataSources[rulesSourceName].result;
getAllRulesSourceNames().map((rulesSourceName) => {
const dataSourceConfig = dataSources[rulesSourceName].result;
if (!dataSourceConfig) {
return;
}
if (!dataSourceConfig) {
return;
}
if (force || !promRules[rulesSourceName]?.loading) {
dispatch(fetchPromRulesAction({ rulesSourceName }));
}
if ((force || !rulerRules[rulesSourceName]?.loading) && dataSourceConfig.rulerConfig) {
dispatch(fetchRulerRulesAction({ rulesSourceName }));
}
});
if (force || !promRules[rulesSourceName]?.loading) {
dispatch(fetchPromRulesAction({ rulesSourceName }));
}
if ((force || !rulerRules[rulesSourceName]?.loading) && dataSourceConfig.rulerConfig) {
dispatch(fetchRulerRulesAction({ rulesSourceName }));
}
})
);
};
}