Alerting: Add oncall contact point type and narrow create hook function (#107711)
This commit is contained in:
@@ -51,7 +51,26 @@ type SlackIntegration = OverrideProperties<
|
||||
}
|
||||
>;
|
||||
|
||||
export type Integration = EmailIntegration | SlackIntegration | GenericIntegration;
|
||||
// Based on https://github.com/grafana/alerting/blob/main/receivers/oncall/config.go#L14-L27
|
||||
type OnCallIntegration = OverrideProperties<
|
||||
GenericIntegration,
|
||||
{
|
||||
type: 'OnCall';
|
||||
settings: {
|
||||
url: string;
|
||||
httpMethod?: 'POST' | 'PUT';
|
||||
maxAlerts?: number;
|
||||
authorization_scheme?: string;
|
||||
authorization_credentials?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
title?: string;
|
||||
message?: string;
|
||||
};
|
||||
}
|
||||
>;
|
||||
|
||||
export type Integration = EmailIntegration | SlackIntegration | OnCallIntegration | GenericIntegration;
|
||||
|
||||
// Enhanced version of ContactPoint with typed integrations
|
||||
// ⚠️ MergeDeep does not check if the property you are overriding exists in the base type and there is no "DeepOverrideProperties" helper
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ import { chain } from 'lodash';
|
||||
import { Combobox, ComboboxOption } from '@grafana/ui';
|
||||
|
||||
import type { ContactPoint } from '../../../api/v0alpha1/types';
|
||||
import { useListContactPointsv0alpha1 } from '../../hooks/useContactPoints';
|
||||
import { useListContactPoints } from '../../hooks/v0alpha1/useContactPoints';
|
||||
import { getContactPointDescription } from '../../utils';
|
||||
|
||||
import { CustomComboBoxProps } from './ComboBox.types';
|
||||
@@ -17,7 +17,7 @@ export type ContactPointSelectorProps = CustomComboBoxProps<ContactPoint>;
|
||||
* @TODO make ComboBox accept a ReactNode so we can use icons and such
|
||||
*/
|
||||
function ContactPointSelector(props: ContactPointSelectorProps) {
|
||||
const { currentData: contactPoints, isLoading } = useListContactPointsv0alpha1();
|
||||
const { currentData: contactPoints, isLoading } = useListContactPoints();
|
||||
|
||||
// Create a mapping of options with their corresponding contact points
|
||||
const contactPointOptions = chain(contactPoints?.items)
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { type TypedUseQueryHookResult, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { type ListReceiverApiArg, alertingAPI } from '../../api/v0alpha1/api.gen';
|
||||
import type { EnhancedListReceiverApiResponse } from '../../api/v0alpha1/types';
|
||||
|
||||
// this is a workaround for the fact that the generated types are not narrow enough
|
||||
type EnhancedHookResult = TypedUseQueryHookResult<
|
||||
EnhancedListReceiverApiResponse,
|
||||
ListReceiverApiArg,
|
||||
ReturnType<typeof fetchBaseQuery>
|
||||
>;
|
||||
|
||||
/**
|
||||
* useListContactPoints is a hook that fetches a list of contact points
|
||||
*
|
||||
* This function wraps the alertingAPI.useListReceiverQuery with proper typing
|
||||
* to ensure that the returned ContactPoints are correctly typed in the data.items array.
|
||||
*
|
||||
* It automatically uses the configured namespace for the query.
|
||||
*/
|
||||
function useListContactPointsv0alpha1() {
|
||||
return alertingAPI.useListReceiverQuery<EnhancedHookResult>({});
|
||||
}
|
||||
|
||||
export { useListContactPointsv0alpha1 };
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
type TypedUseMutationResult,
|
||||
type TypedUseQueryHookResult,
|
||||
fetchBaseQuery,
|
||||
} from '@reduxjs/toolkit/query/react';
|
||||
import { OverrideProperties } from 'type-fest';
|
||||
|
||||
import { CreateReceiverApiArg, type ListReceiverApiArg, alertingAPI } from '../../../api/v0alpha1/api.gen';
|
||||
import type { ContactPoint, EnhancedListReceiverApiResponse } from '../../../api/v0alpha1/types';
|
||||
|
||||
// this is a workaround for the fact that the generated types are not narrow enough
|
||||
type ListContactPointsHookResult = TypedUseQueryHookResult<
|
||||
EnhancedListReceiverApiResponse,
|
||||
ListReceiverApiArg,
|
||||
ReturnType<typeof fetchBaseQuery>
|
||||
>;
|
||||
|
||||
/**
|
||||
* useListContactPoints is a hook that fetches a list of contact points
|
||||
*
|
||||
* This function wraps the alertingAPI.useListReceiverQuery with proper typing
|
||||
* to ensure that the returned ContactPoints are correctly typed in the data.items array.
|
||||
*
|
||||
* It automatically uses the configured namespace for the query.
|
||||
*/
|
||||
export function useListContactPoints() {
|
||||
return alertingAPI.useListReceiverQuery<ListContactPointsHookResult>({});
|
||||
}
|
||||
|
||||
// type narrowing mutations requires us to define a few helper types
|
||||
type CreateContactPointArgs = OverrideProperties<
|
||||
CreateReceiverApiArg,
|
||||
{ receiver: Omit<ContactPoint, 'status' | 'metadata'> }
|
||||
>;
|
||||
|
||||
type CreateContactPointMutation = TypedUseMutationResult<
|
||||
ContactPoint,
|
||||
CreateContactPointArgs,
|
||||
ReturnType<typeof fetchBaseQuery>
|
||||
>;
|
||||
|
||||
type UseCreateContactPointOptions = Parameters<
|
||||
typeof alertingAPI.endpoints.createReceiver.useMutation<CreateContactPointMutation>
|
||||
>[0];
|
||||
|
||||
/**
|
||||
* useCreateContactPoint is a hook that creates a new contact point with one or more integrations
|
||||
*
|
||||
* This function wraps the alertingAPI.useCreateReceiverMutation with proper typing
|
||||
* to ensure that the payload supports type narrowing.
|
||||
*/
|
||||
export function useCreateContactPoint(options?: UseCreateContactPointOptions) {
|
||||
const [updateFn, result] = alertingAPI.endpoints.createReceiver.useMutation<CreateContactPointMutation>(options);
|
||||
|
||||
const typedUpdateFn = (args: CreateContactPointArgs) => {
|
||||
// @ts-expect-error this one is just impossible for me to figure out
|
||||
const response = updateFn(args);
|
||||
return response;
|
||||
};
|
||||
|
||||
return [typedUpdateFn, result] as const;
|
||||
}
|
||||
@@ -4,14 +4,8 @@
|
||||
|
||||
// Contact Points
|
||||
export * from './grafana/api/v0alpha1/types';
|
||||
export { useListContactPointsv0alpha1 } from './grafana/contactPoints/hooks/useContactPoints';
|
||||
export { useListContactPoints } from './grafana/contactPoints/hooks/v0alpha1/useContactPoints';
|
||||
export { ContactPointSelector } from './grafana/contactPoints/components/ContactPointSelector/ContactPointSelector';
|
||||
|
||||
// Low-level API hooks
|
||||
export { alertingAPI as alertingAPIv0alpha1 } from './grafana/api/v0alpha1/api.gen';
|
||||
|
||||
// model factories / mocks
|
||||
export * as mocksV0alpha1 from './grafana/api/v0alpha1/mocks/fakes/Receivers';
|
||||
|
||||
// MSW handlers
|
||||
export * as handlersV0alpha1 from './grafana/api/v0alpha1/mocks/handlers';
|
||||
export { alertingAPI } from './grafana/api/v0alpha1/api.gen';
|
||||
|
||||
@@ -2,13 +2,13 @@ import { configureStore } from '@reduxjs/toolkit';
|
||||
import { useEffect } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { alertingAPIv0alpha1 } from '../src/unstable';
|
||||
import { alertingAPI } from '../src/unstable';
|
||||
|
||||
// create an empty store
|
||||
export const store = configureStore({
|
||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(alertingAPIv0alpha1.middleware),
|
||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(alertingAPI.middleware),
|
||||
reducer: {
|
||||
[alertingAPIv0alpha1.reducerPath]: alertingAPIv0alpha1.reducer,
|
||||
[alertingAPI.reducerPath]: alertingAPI.reducer,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ export const getDefaultWrapper = () => {
|
||||
function useResetQueryCacheAfterUnmount() {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
store.dispatch(alertingAPIv0alpha1.util.resetApiState());
|
||||
store.dispatch(alertingAPI.util.resetApiState());
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ReducersMapObject } from '@reduxjs/toolkit';
|
||||
import { AnyAction, combineReducers } from 'redux';
|
||||
|
||||
import { alertingAPIv0alpha1 } from '@grafana/alerting/unstable';
|
||||
import { alertingAPI as alertingPackageAPI } from '@grafana/alerting/unstable';
|
||||
import sharedReducers from 'app/core/reducers';
|
||||
import ldapReducers from 'app/features/admin/state/reducers';
|
||||
import alertingReducers from 'app/features/alerting/state/reducers';
|
||||
@@ -61,7 +61,7 @@ const rootReducers = {
|
||||
...authConfigReducers,
|
||||
plugins: pluginsReducer,
|
||||
[alertingApi.reducerPath]: alertingApi.reducer,
|
||||
[alertingAPIv0alpha1.reducerPath]: alertingAPIv0alpha1.reducer,
|
||||
[alertingPackageAPI.reducerPath]: alertingPackageAPI.reducer,
|
||||
[publicDashboardApi.reducerPath]: publicDashboardApi.reducer,
|
||||
[browseDashboardsAPI.reducerPath]: browseDashboardsAPI.reducer,
|
||||
[cloudMigrationAPI.reducerPath]: cloudMigrationAPI.reducer,
|
||||
|
||||
+2
-5
@@ -3,10 +3,7 @@ import { isEmpty } from 'lodash';
|
||||
import { useEffect } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
import {
|
||||
ContactPointSelector as GrafanaManagedContactPointSelector,
|
||||
alertingAPIv0alpha1,
|
||||
} from '@grafana/alerting/unstable';
|
||||
import { ContactPointSelector as GrafanaManagedContactPointSelector, alertingAPI } from '@grafana/alerting/unstable';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Field, FieldValidationMessage, Stack, TextLink } from '@grafana/ui';
|
||||
import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
|
||||
@@ -24,7 +21,7 @@ export function ContactPointSelector({ alertManager }: ContactPointSelectorProps
|
||||
|
||||
// check if the contact point still exists, we'll use listReceiver to check if the contact point exists because getReceiver doesn't work with
|
||||
// contact point titles but with UUIDs (which is not what we store on the alert rule definition)
|
||||
const { currentData, status } = alertingAPIv0alpha1.endpoints.listReceiver.useQuery({
|
||||
const { currentData, status } = alertingAPI.endpoints.listReceiver.useQuery({
|
||||
fieldSelector: `spec.title=${contactPointInForm}`,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ComponentProps } from 'react';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
import { alertingAPIv0alpha1 } from '@grafana/alerting/unstable';
|
||||
import { alertingAPI } from '@grafana/alerting/unstable';
|
||||
import { TextLink } from '@grafana/ui';
|
||||
|
||||
import { makeEditContactPointLink } from '../../utils/misc';
|
||||
@@ -12,7 +12,7 @@ interface ContactPointLinkProps extends Omit<ComponentProps<typeof TextLink>, 'h
|
||||
|
||||
export const ContactPointLink = ({ name, ...props }: ContactPointLinkProps) => {
|
||||
// find receiver by name – since this is what we store in the alert rule definition
|
||||
const { currentData, isLoading, isSuccess } = alertingAPIv0alpha1.endpoints.listReceiver.useQuery({
|
||||
const { currentData, isLoading, isSuccess } = alertingAPI.endpoints.listReceiver.useQuery({
|
||||
fieldSelector: `spec.title=${name}`,
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { configureStore as reduxConfigureStore, createListenerMiddleware } from
|
||||
import { setupListeners } from '@reduxjs/toolkit/query';
|
||||
import { Middleware } from 'redux';
|
||||
|
||||
import { alertingAPIv0alpha1 } from '@grafana/alerting/unstable';
|
||||
import { alertingAPI as alertingPackageAPI } from '@grafana/alerting/unstable';
|
||||
import { browseDashboardsAPI } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
|
||||
import { publicDashboardApi } from 'app/features/dashboard/api/publicDashboardApi';
|
||||
import { cloudMigrationAPI } from 'app/features/migrate-to-cloud/api';
|
||||
@@ -43,7 +43,7 @@ export function configureStore(initialState?: Partial<StoreState>) {
|
||||
getDefaultMiddleware({ thunk: true, serializableCheck: false, immutableCheck: false }).concat(
|
||||
listenerMiddleware.middleware,
|
||||
alertingApi.middleware,
|
||||
alertingAPIv0alpha1.middleware,
|
||||
alertingPackageAPI.middleware,
|
||||
publicDashboardApi.middleware,
|
||||
browseDashboardsAPI.middleware,
|
||||
cloudMigrationAPI.middleware,
|
||||
|
||||
Reference in New Issue
Block a user