Alerting: Move alerting RTKQ client to api-clients package (#114546)

This commit is contained in:
Gilles De Mey
2026-01-06 14:25:36 +00:00
committed by GitHub
parent 92464b2dc8
commit bbaf91ed9c
44 changed files with 324 additions and 623 deletions
@@ -1,18 +0,0 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { getAPIBaseURL, getAPIReducerPath } from '../../util';
import { GROUP, VERSION } from './const';
const baseUrl = getAPIBaseURL(GROUP, VERSION);
const reducerPath = getAPIReducerPath(GROUP, VERSION);
export const api = createApi({
reducerPath,
baseQuery: fetchBaseQuery({
// Set URL correctly so MSW can intercept requests
// https://mswjs.io/docs/runbook#rtk-query-requests-are-not-intercepted
baseUrl: new URL(baseUrl, globalThis.location.origin).href,
}),
endpoints: () => ({}),
});
@@ -1,2 +0,0 @@
export const VERSION = 'v0alpha1' as const;
export const GROUP = 'notifications.alerting.grafana.app' as const;
@@ -1,8 +1,9 @@
import { faker } from '@faker-js/faker';
import { Factory } from 'fishery';
import { API_GROUP, API_VERSION } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { DEFAULT_NAMESPACE, generateResourceVersion, generateTitle, generateUID } from '../../../../../mocks/util';
import { GROUP, VERSION } from '../../const';
import {
ContactPoint,
ContactPointMetadataAnnotations,
@@ -14,7 +15,7 @@ import { AlertingEntityMetadataAnnotationsFactory } from './common';
export const ListReceiverApiResponseFactory = Factory.define<EnhancedListReceiverApiResponse>(() => ({
kind: 'ReceiverList',
apiVersion: `${GROUP}/${VERSION}`,
apiVersion: `${API_GROUP}/${API_VERSION}`,
metadata: {
resourceVersion: generateResourceVersion(),
},
@@ -26,7 +27,7 @@ export const ContactPointFactory = Factory.define<ContactPoint>(() => {
return {
kind: 'Receiver',
apiVersion: `${GROUP}/${VERSION}`,
apiVersion: `${API_GROUP}/${API_VERSION}`,
metadata: {
name: btoa(title),
namespace: DEFAULT_NAMESPACE,
@@ -1,13 +1,17 @@
import { HttpResponse, http } from 'msw';
import {
API_GROUP,
API_VERSION,
CreateReceiverApiResponse,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { getAPIBaseURLForMocks } from '../../../../../../mocks/util';
import { CreateReceiverApiResponse } from '../../../../v0alpha1/notifications.api.gen';
import { GROUP, VERSION } from '../../../const';
export function createReceiverHandler(
data: CreateReceiverApiResponse | ((info: Parameters<Parameters<typeof http.post>[1]>[0]) => Response)
) {
return http.post(getAPIBaseURLForMocks(GROUP, VERSION, '/receivers'), function handler(info) {
return http.post(getAPIBaseURLForMocks(API_GROUP, API_VERSION, '/receivers'), function handler(info) {
if (typeof data === 'function') {
return data(info);
}
@@ -1,13 +1,17 @@
import { HttpResponse, http } from 'msw';
import {
API_GROUP,
API_VERSION,
DeleteReceiverApiResponse,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { getAPIBaseURLForMocks } from '../../../../../../mocks/util';
import { DeleteReceiverApiResponse } from '../../../../v0alpha1/notifications.api.gen';
import { GROUP, VERSION } from '../../../const';
export function deleteReceiverHandler(
data: DeleteReceiverApiResponse | ((info: Parameters<Parameters<typeof http.delete>[1]>[0]) => Response)
) {
return http.delete(getAPIBaseURLForMocks(GROUP, VERSION, '/receivers/:name'), function handler(info) {
return http.delete(getAPIBaseURLForMocks(API_GROUP, API_VERSION, '/receivers/:name'), function handler(info) {
if (typeof data === 'function') {
return data(info);
}
@@ -1,13 +1,17 @@
import { HttpResponse, http } from 'msw';
import {
API_GROUP,
API_VERSION,
DeletecollectionReceiverApiResponse,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { getAPIBaseURLForMocks } from '../../../../../../mocks/util';
import { DeletecollectionReceiverApiResponse } from '../../../../v0alpha1/notifications.api.gen';
import { GROUP, VERSION } from '../../../const';
export function deletecollectionReceiverHandler(
data: DeletecollectionReceiverApiResponse | ((info: Parameters<Parameters<typeof http.delete>[1]>[0]) => Response)
) {
return http.delete(getAPIBaseURLForMocks(GROUP, VERSION, '/receivers'), function handler(info) {
return http.delete(getAPIBaseURLForMocks(API_GROUP, API_VERSION, '/receivers'), function handler(info) {
if (typeof data === 'function') {
return data(info);
}
@@ -1,13 +1,17 @@
import { HttpResponse, http } from 'msw';
import {
API_GROUP,
API_VERSION,
GetReceiverApiResponse,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { getAPIBaseURLForMocks } from '../../../../../../mocks/util';
import { GetReceiverApiResponse } from '../../../../v0alpha1/notifications.api.gen';
import { GROUP, VERSION } from '../../../const';
export function getReceiverHandler(
data: GetReceiverApiResponse | ((info: Parameters<Parameters<typeof http.get>[1]>[0]) => Response)
) {
return http.get(getAPIBaseURLForMocks(GROUP, VERSION, '/receivers/:name'), function handler(info) {
return http.get(getAPIBaseURLForMocks(API_GROUP, API_VERSION, '/receivers/:name'), function handler(info) {
if (typeof data === 'function') {
return data(info);
}
@@ -1,13 +1,14 @@
import { HttpResponse, http } from 'msw';
import { API_GROUP, API_VERSION } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { getAPIBaseURLForMocks } from '../../../../../../mocks/util';
import { GROUP, VERSION } from '../../../const';
import { EnhancedListReceiverApiResponse } from '../../../types';
export function listReceiverHandler(
data: EnhancedListReceiverApiResponse | ((info: Parameters<Parameters<typeof http.get>[1]>[0]) => Response)
) {
return http.get(getAPIBaseURLForMocks(GROUP, VERSION, '/receivers'), function handler(info) {
return http.get(getAPIBaseURLForMocks(API_GROUP, API_VERSION, '/receivers'), function handler(info) {
if (typeof data === 'function') {
return data(info);
}
@@ -1,13 +1,17 @@
import { HttpResponse, http } from 'msw';
import {
API_GROUP,
API_VERSION,
ReplaceReceiverApiResponse,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { getAPIBaseURLForMocks } from '../../../../../../mocks/util';
import { ReplaceReceiverApiResponse } from '../../../../v0alpha1/notifications.api.gen';
import { GROUP, VERSION } from '../../../const';
export function replaceReceiverHandler(
data: ReplaceReceiverApiResponse | ((info: Parameters<Parameters<typeof http.put>[1]>[0]) => Response)
) {
return http.put(getAPIBaseURLForMocks(GROUP, VERSION, '/receivers/:name'), function handler(info) {
return http.put(getAPIBaseURLForMocks(API_GROUP, API_VERSION, '/receivers/:name'), function handler(info) {
if (typeof data === 'function') {
return data(info);
}
@@ -1,13 +1,17 @@
import { HttpResponse, http } from 'msw';
import {
API_GROUP,
API_VERSION,
UpdateReceiverApiResponse,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { getAPIBaseURLForMocks } from '../../../../../../mocks/util';
import { UpdateReceiverApiResponse } from '../../../../v0alpha1/notifications.api.gen';
import { GROUP, VERSION } from '../../../const';
export function updateReceiverHandler(
data: UpdateReceiverApiResponse | ((info: Parameters<Parameters<typeof http.patch>[1]>[0]) => Response)
) {
return http.patch(getAPIBaseURLForMocks(GROUP, VERSION, '/receivers/:name'), function handler(info) {
return http.patch(getAPIBaseURLForMocks(API_GROUP, API_VERSION, '/receivers/:name'), function handler(info) {
if (typeof data === 'function') {
return data(info);
}
File diff suppressed because it is too large Load Diff
@@ -3,7 +3,11 @@
*/
import { MergeDeep, MergeExclusive, OverrideProperties } from 'type-fest';
import type { ListReceiverApiResponse, Receiver, ReceiverIntegration } from './notifications.api.gen';
import type {
ListReceiverApiResponse,
Receiver,
ReceiverIntegration,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
type GenericIntegration = OverrideProperties<
ReceiverIntegration,
@@ -1,18 +0,0 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { getAPIBaseURL, getAPIReducerPath } from '../../util';
import { GROUP, VERSION } from './const';
const baseUrl = getAPIBaseURL(GROUP, VERSION);
const reducerPath = getAPIReducerPath(GROUP, VERSION);
export const api = createApi({
reducerPath,
baseQuery: fetchBaseQuery({
// Set URL correctly so MSW can intercept requests
// https://mswjs.io/docs/runbook#rtk-query-requests-are-not-intercepted
baseUrl: new URL(baseUrl, globalThis.location.origin).href,
}),
endpoints: () => ({}),
});
@@ -1,2 +0,0 @@
export const VERSION = 'v0alpha1' as const;
export const GROUP = 'rules.alerting.grafana.app' as const;
File diff suppressed because it is too large Load Diff
@@ -7,9 +7,10 @@ import { OverrideProperties } from 'type-fest';
import {
CreateReceiverApiArg,
type ListReceiverApiArg,
notificationsAPI,
} from '../../../api/notifications/v0alpha1/notifications.api.gen';
ListReceiverApiArg,
generatedAPI as notificationsAPIv0alpha1,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import type { ContactPoint, EnhancedListReceiverApiResponse } from '../../../api/notifications/v0alpha1/types';
// this is a workaround for the fact that the generated types are not narrow enough
@@ -22,17 +23,17 @@ type ListContactPointsHookResult = TypedUseQueryHookResult<
// Type for the options that can be passed to the hook
// Based on the pattern used for mutation options in this file
type ListContactPointsQueryArgs = Parameters<
typeof notificationsAPI.endpoints.listReceiver.useQuery<ListContactPointsHookResult>
typeof notificationsAPIv0alpha1.endpoints.listReceiver.useQuery<ListContactPointsHookResult>
>[0];
type ListContactPointsQueryOptions = Parameters<
typeof notificationsAPI.endpoints.listReceiver.useQuery<ListContactPointsHookResult>
typeof notificationsAPIv0alpha1.endpoints.listReceiver.useQuery<ListContactPointsHookResult>
>[1];
/**
* useListContactPoints is a hook that fetches a list of contact points
*
* This function wraps the notificationsAPI.useListReceiverQuery with proper typing
* This function wraps the notificationsAPIv0alpha1.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.
@@ -43,8 +44,8 @@ type ListContactPointsQueryOptions = Parameters<
export function useListContactPoints(
queryArgs: ListContactPointsQueryArgs = {},
queryOptions: ListContactPointsQueryOptions = {}
) {
return notificationsAPI.useListReceiverQuery<ListContactPointsHookResult>(queryArgs, queryOptions);
): ListContactPointsHookResult {
return notificationsAPIv0alpha1.useListReceiverQuery<ListContactPointsHookResult>(queryArgs, queryOptions);
}
// type narrowing mutations requires us to define a few helper types
@@ -60,7 +61,7 @@ type CreateContactPointMutation = TypedUseMutationResult<
>;
type UseCreateContactPointOptions = Parameters<
typeof notificationsAPI.endpoints.createReceiver.useMutation<CreateContactPointMutation>
typeof notificationsAPIv0alpha1.endpoints.createReceiver.useMutation<CreateContactPointMutation>
>[0];
/**
@@ -69,8 +70,16 @@ type UseCreateContactPointOptions = Parameters<
* This function wraps the notificationsAPI.useCreateReceiverMutation with proper typing
* to ensure that the payload supports type narrowing.
*/
export function useCreateContactPoint(options?: UseCreateContactPointOptions) {
const [updateFn, result] = notificationsAPI.endpoints.createReceiver.useMutation<CreateContactPointMutation>(options);
export function useCreateContactPoint(
options?: UseCreateContactPointOptions
): readonly [
(
args: CreateContactPointArgs
) => ReturnType<ReturnType<typeof notificationsAPIv0alpha1.endpoints.createReceiver.useMutation>[0]>,
ReturnType<typeof notificationsAPIv0alpha1.endpoints.createReceiver.useMutation<CreateContactPointMutation>>[1],
] {
const [updateFn, result] =
notificationsAPIv0alpha1.endpoints.createReceiver.useMutation<CreateContactPointMutation>(options);
const typedUpdateFn = (args: CreateContactPointArgs) => {
// @ts-expect-error this one is just impossible for me to figure out
@@ -1,6 +1,7 @@
import { countBy, isEmpty } from 'lodash';
import { Receiver } from '../api/notifications/v0alpha1/notifications.api.gen';
import { Receiver } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { ContactPoint } from '../api/notifications/v0alpha1/types';
/**
@@ -1,4 +1,4 @@
import { RoutingTreeMatcher } from '../api/notifications/v0alpha1/notifications.api.gen';
import { RoutingTreeMatcher } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
export type Label = [string, string];
@@ -1,6 +1,6 @@
import { VERSION } from '../../api/notifications/v0alpha1/const';
import { API_VERSION, RoutingTree } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { LabelMatcherFactory, RouteFactory } from '../../api/notifications/v0alpha1/mocks/fakes/Routes';
import { RoutingTree } from '../../api/notifications/v0alpha1/notifications.api.gen';
import { Label } from '../../matchers/types';
import { matchInstancesToRouteTrees } from './useMatchPolicies';
@@ -16,7 +16,7 @@ describe('matchInstancesToRouteTrees', () => {
const trees: RoutingTree[] = [
{
kind: 'RoutingTree',
apiVersion: VERSION,
apiVersion: API_VERSION,
metadata: { name: treeName },
spec: {
defaults: {
@@ -24,7 +24,6 @@ describe('matchInstancesToRouteTrees', () => {
},
routes: [route],
},
status: {},
},
];
@@ -51,7 +50,7 @@ describe('matchInstancesToRouteTrees', () => {
const trees: RoutingTree[] = [
{
kind: 'RoutingTree',
apiVersion: VERSION,
apiVersion: API_VERSION,
metadata: { name: treeName },
spec: {
defaults: {
@@ -59,7 +58,6 @@ describe('matchInstancesToRouteTrees', () => {
},
routes: [route],
},
status: {},
},
];
@@ -1,6 +1,10 @@
import { useCallback } from 'react';
import { RoutingTree, notificationsAPI } from '../../api/notifications/v0alpha1/notifications.api.gen';
import {
RoutingTree,
generatedAPI as notificationsAPIv0alpha1,
} from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { Label } from '../../matchers/types';
import { USER_DEFINED_TREE_NAME } from '../consts';
import { Route, RouteWithID } from '../types';
@@ -24,6 +28,11 @@ export type InstanceMatchResult = {
matchedRoutes: RouteMatch[];
};
interface UseMatchInstancesToRouteTreesReturnType
extends ReturnType<typeof notificationsAPIv0alpha1.endpoints.listRoutingTree.useQuery> {
matchInstancesToRouteTrees: (instances: Label[][]) => InstanceMatchResult[];
}
/**
* React hook that finds notification policy routes in all routing trees that match the provided set of alert instances.
*
@@ -35,8 +44,8 @@ export type InstanceMatchResult = {
* @returns An object containing a `matchInstancesToRoutingTrees` function that takes alert instances
* and returns an array of InstanceMatchResult objects, each containing the matched routes and matching details
*/
export function useMatchInstancesToRouteTrees() {
const { data, ...rest } = notificationsAPI.endpoints.listRoutingTree.useQuery(
export function useMatchInstancesToRouteTrees(): UseMatchInstancesToRouteTreesReturnType {
const { data, ...rest } = notificationsAPIv0alpha1.endpoints.listRoutingTree.useQuery(
{},
{
refetchOnFocus: true,
@@ -1,6 +1,7 @@
import { OverrideProperties } from 'type-fest';
import { RoutingTreeRoute } from '../api/notifications/v0alpha1/notifications.api.gen';
import { RoutingTreeRoute } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { LabelMatcher } from '../matchers/types';
// type-narrow the route tree
@@ -1,6 +1,7 @@
import { groupBy, isArray, pick, reduce, uniqueId } from 'lodash';
import { RoutingTree, RoutingTreeRoute } from '../api/notifications/v0alpha1/notifications.api.gen';
import { RoutingTree, RoutingTreeRoute } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
import { Label } from '../matchers/types';
import { LabelMatchDetails, matchLabels } from '../matchers/utils';
+2 -2
View File
@@ -19,5 +19,5 @@ export { type LabelMatcher, type Label } from './grafana/matchers/types';
export { matchLabelsSet, matchLabels, isLabelMatch, type LabelMatchDetails } from './grafana/matchers/utils';
// API endpoints
export { notificationsAPI as notificationsAPIv0alpha1 } from './grafana/api/notifications/v0alpha1/notifications.api.gen';
export { rulesAPI as rulesAPIv0alpha1 } from './grafana/api/rules/v0alpha1/rules.api.gen';
export { generatedAPI as notificationsAPIv0alpha1 } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';
export { generatedAPI as rulesAPIv0alpha1 } from '@grafana/api-clients/rtkq/rules.alerting/v0alpha1';