mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
feat(slo): refactor fetch slo definitions hook (#164466)
This commit is contained in:
parent
7ee14bc1ae
commit
b270602601
8 changed files with 138 additions and 73 deletions
|
@ -170,6 +170,24 @@ const fetchHistoricalSummaryResponseSchema = t.array(
|
|||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* The query params schema for /internal/observability/slo/_definitions
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const findSloDefinitionsParamsSchema = t.type({
|
||||
query: t.type({
|
||||
search: t.string,
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* The response schema for /internal/observability/slo/_definitions
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const findSloDefinitionsResponseSchema = t.array(sloResponseSchema);
|
||||
|
||||
const getSLODiagnosisParamsSchema = t.type({
|
||||
path: t.type({ id: t.string }),
|
||||
});
|
||||
|
@ -229,6 +247,13 @@ type FetchHistoricalSummaryParams = t.TypeOf<typeof fetchHistoricalSummaryParams
|
|||
type FetchHistoricalSummaryResponse = t.OutputOf<typeof fetchHistoricalSummaryResponseSchema>;
|
||||
type HistoricalSummaryResponse = t.OutputOf<typeof historicalSummarySchema>;
|
||||
|
||||
/**
|
||||
* The response type for /internal/observability/slo/_definitions
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
type FindSloDefinitionsResponse = t.OutputOf<typeof findSloDefinitionsResponseSchema>;
|
||||
|
||||
type GetPreviewDataParams = t.TypeOf<typeof getPreviewDataParamsSchema.props.body>;
|
||||
type GetPreviewDataResponse = t.OutputOf<typeof getPreviewDataResponseSchema>;
|
||||
|
||||
|
@ -257,6 +282,8 @@ export {
|
|||
getSLOResponseSchema,
|
||||
fetchHistoricalSummaryParamsSchema,
|
||||
fetchHistoricalSummaryResponseSchema,
|
||||
findSloDefinitionsParamsSchema,
|
||||
findSloDefinitionsResponseSchema,
|
||||
manageSLOParamsSchema,
|
||||
sloResponseSchema,
|
||||
sloWithSummaryResponseSchema,
|
||||
|
@ -281,6 +308,7 @@ export type {
|
|||
FetchHistoricalSummaryParams,
|
||||
FetchHistoricalSummaryResponse,
|
||||
HistoricalSummaryResponse,
|
||||
FindSloDefinitionsResponse,
|
||||
ManageSLOParams,
|
||||
SLOResponse,
|
||||
SLOWithSummaryResponse,
|
||||
|
|
|
@ -34,6 +34,7 @@ export const sloKeys = {
|
|||
historicalSummaries: () => [...sloKeys.all, 'historicalSummary'] as const,
|
||||
historicalSummary: (list: Array<{ sloId: string; instanceId: string }>) =>
|
||||
[...sloKeys.historicalSummaries(), list] as const,
|
||||
definitions: (search: string) => [...sloKeys.all, 'definitions', search] as const,
|
||||
globalDiagnosis: () => [...sloKeys.all, 'globalDiagnosis'] as const,
|
||||
burnRates: (sloId: string, instanceId: string | undefined) =>
|
||||
[...sloKeys.all, 'burnRates', sloId, instanceId] as const,
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FindSloDefinitionsResponse, SLOResponse } from '@kbn/slo-schema';
|
||||
import {
|
||||
QueryObserverResult,
|
||||
RefetchOptions,
|
||||
RefetchQueryFilters,
|
||||
useQuery,
|
||||
} from '@tanstack/react-query';
|
||||
import { SLOResponse } from '@kbn/slo-schema';
|
||||
import { useKibana } from '../../utils/kibana_react';
|
||||
import { sloKeys } from './query_key_factory';
|
||||
|
||||
export interface UseFetchSloDefinitionsResponse {
|
||||
isLoading: boolean;
|
||||
|
@ -26,31 +27,33 @@ export interface UseFetchSloDefinitionsResponse {
|
|||
|
||||
interface Params {
|
||||
name?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export function useFetchSloDefinitions({
|
||||
name = '',
|
||||
size = 10,
|
||||
}: Params): UseFetchSloDefinitionsResponse {
|
||||
const { savedObjects } = useKibana().services;
|
||||
export function useFetchSloDefinitions({ name = '' }: Params): UseFetchSloDefinitionsResponse {
|
||||
const { http } = useKibana().services;
|
||||
const search = name.endsWith('*') ? name : `${name}*`;
|
||||
|
||||
const { isLoading, isError, isSuccess, data, refetch } = useQuery({
|
||||
queryKey: ['fetchSloDefinitions', search],
|
||||
queryFn: async () => {
|
||||
queryKey: sloKeys.definitions(search),
|
||||
queryFn: async ({ signal }) => {
|
||||
try {
|
||||
const response = await savedObjects.client.find<SLOResponse>({
|
||||
type: 'slo',
|
||||
search,
|
||||
searchFields: ['name'],
|
||||
perPage: size,
|
||||
});
|
||||
return response.savedObjects.map((so) => so.attributes);
|
||||
const response = await http.get<FindSloDefinitionsResponse>(
|
||||
'/internal/observability/slos/_definitions',
|
||||
{
|
||||
query: {
|
||||
search,
|
||||
},
|
||||
signal,
|
||||
}
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new Error(`Something went wrong. Error: ${error}`);
|
||||
}
|
||||
},
|
||||
retry: false,
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
return { isLoading, isError, isSuccess, data, refetch };
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
createSLOParamsSchema,
|
||||
deleteSLOParamsSchema,
|
||||
fetchHistoricalSummaryParamsSchema,
|
||||
findSloDefinitionsParamsSchema,
|
||||
findSLOParamsSchema,
|
||||
getPreviewDataParamsSchema,
|
||||
getSLOBurnRatesParamsSchema,
|
||||
|
@ -31,6 +32,7 @@ import {
|
|||
UpdateSLO,
|
||||
} from '../../services/slo';
|
||||
import { FetchHistoricalSummary } from '../../services/slo/fetch_historical_summary';
|
||||
import { FindSLODefinitions } from '../../services/slo/find_slo_definitions';
|
||||
import { getBurnRates } from '../../services/slo/get_burn_rates';
|
||||
import { getGlobalDiagnosis, getSloDiagnosis } from '../../services/slo/get_diagnosis';
|
||||
import { GetPreviewData } from '../../services/slo/get_preview_data';
|
||||
|
@ -57,9 +59,13 @@ const transformGenerators: Record<IndicatorTypes, TransformGenerator> = {
|
|||
'sli.histogram.custom': new HistogramTransformGenerator(),
|
||||
};
|
||||
|
||||
const isLicenseAtLeastPlatinum = async (context: ObservabilityRequestHandlerContext) => {
|
||||
const assertPlatinumLicense = async (context: ObservabilityRequestHandlerContext) => {
|
||||
const licensing = await context.licensing;
|
||||
return licensing.license.hasAtLeast('platinum');
|
||||
const hasCorrectLicense = licensing.license.hasAtLeast('platinum');
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
};
|
||||
|
||||
const createSLORoute = createObservabilityServerRoute({
|
||||
|
@ -69,11 +75,7 @@ const createSLORoute = createObservabilityServerRoute({
|
|||
},
|
||||
params: createSLOParamsSchema,
|
||||
handler: async ({ context, params, logger }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
|
@ -94,11 +96,7 @@ const updateSLORoute = createObservabilityServerRoute({
|
|||
},
|
||||
params: updateSLOParamsSchema,
|
||||
handler: async ({ context, params, logger }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
|
@ -126,11 +124,7 @@ const deleteSLORoute = createObservabilityServerRoute({
|
|||
logger,
|
||||
dependencies: { getRulesClientWithRequest },
|
||||
}) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
|
@ -152,11 +146,7 @@ const getSLORoute = createObservabilityServerRoute({
|
|||
},
|
||||
params: getSLOParamsSchema,
|
||||
handler: async ({ context, params }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
|
@ -177,11 +167,7 @@ const enableSLORoute = createObservabilityServerRoute({
|
|||
},
|
||||
params: manageSLOParamsSchema,
|
||||
handler: async ({ context, params, logger }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
|
@ -203,11 +189,7 @@ const disableSLORoute = createObservabilityServerRoute({
|
|||
},
|
||||
params: manageSLOParamsSchema,
|
||||
handler: async ({ context, params, logger }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
|
@ -229,11 +211,7 @@ const findSLORoute = createObservabilityServerRoute({
|
|||
},
|
||||
params: findSLOParamsSchema,
|
||||
handler: async ({ context, params, logger }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
|
@ -247,6 +225,25 @@ const findSLORoute = createObservabilityServerRoute({
|
|||
},
|
||||
});
|
||||
|
||||
const findSloDefinitionsRoute = createObservabilityServerRoute({
|
||||
endpoint: 'GET /internal/observability/slos/_definitions',
|
||||
options: {
|
||||
tags: ['access:slo_read'],
|
||||
},
|
||||
params: findSloDefinitionsParamsSchema,
|
||||
handler: async ({ context, params }) => {
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const repository = new KibanaSavedObjectsSLORepository(soClient);
|
||||
const findSloDefinitions = new FindSLODefinitions(repository);
|
||||
|
||||
const response = await findSloDefinitions.execute(params.query.search);
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
|
||||
const fetchHistoricalSummary = createObservabilityServerRoute({
|
||||
endpoint: 'POST /internal/observability/slos/_historical_summary',
|
||||
options: {
|
||||
|
@ -254,11 +251,7 @@ const fetchHistoricalSummary = createObservabilityServerRoute({
|
|||
},
|
||||
params: fetchHistoricalSummaryParamsSchema,
|
||||
handler: async ({ context, params }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
|
@ -280,11 +273,7 @@ const getSLOInstancesRoute = createObservabilityServerRoute({
|
|||
},
|
||||
params: getSLOInstancesParamsSchema,
|
||||
handler: async ({ context, params }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
|
@ -342,11 +331,7 @@ const getSloBurnRates = createObservabilityServerRoute({
|
|||
},
|
||||
params: getSLOBurnRatesParamsSchema,
|
||||
handler: async ({ context, params }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
|
@ -370,11 +355,7 @@ const getPreviewData = createObservabilityServerRoute({
|
|||
},
|
||||
params: getPreviewDataParamsSchema,
|
||||
handler: async ({ context, params }) => {
|
||||
const hasCorrectLicense = await isLicenseAtLeastPlatinum(context);
|
||||
|
||||
if (!hasCorrectLicense) {
|
||||
throw forbidden('Platinum license or higher is needed to make use of this feature.');
|
||||
}
|
||||
await assertPlatinumLicense(context);
|
||||
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const service = new GetPreviewData(esClient);
|
||||
|
@ -388,6 +369,7 @@ export const sloRouteRepository = {
|
|||
...disableSLORoute,
|
||||
...enableSLORoute,
|
||||
...fetchHistoricalSummary,
|
||||
...findSloDefinitionsRoute,
|
||||
...findSLORoute,
|
||||
...getSLORoute,
|
||||
...updateSLORoute,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FindSloDefinitionsResponse, findSloDefinitionsResponseSchema } from '@kbn/slo-schema';
|
||||
import { SLORepository } from './slo_repository';
|
||||
|
||||
export class FindSLODefinitions {
|
||||
constructor(private repository: SLORepository) {}
|
||||
|
||||
public async execute(search: string): Promise<FindSloDefinitionsResponse> {
|
||||
const sloList = await this.repository.search(search);
|
||||
return findSloDefinitionsResponseSchema.encode(sloList);
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ const createSLORepositoryMock = (): jest.Mocked<SLORepository> => {
|
|||
findById: jest.fn(),
|
||||
findAllByIds: jest.fn(),
|
||||
deleteById: jest.fn(),
|
||||
search: jest.fn(),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -163,4 +163,20 @@ describe('KibanaSavedObjectsSLORepository', () => {
|
|||
});
|
||||
expect(soClientMock.delete).toHaveBeenCalledWith(SO_SLO_TYPE, SOME_SLO.id);
|
||||
});
|
||||
|
||||
it('searches by name', async () => {
|
||||
const repository = new KibanaSavedObjectsSLORepository(soClientMock);
|
||||
soClientMock.find.mockResolvedValueOnce(soFindResponse([SOME_SLO, ANOTHER_SLO]));
|
||||
|
||||
const results = await repository.search(SOME_SLO.name);
|
||||
|
||||
expect(results).toEqual([SOME_SLO, ANOTHER_SLO]);
|
||||
expect(soClientMock.find).toHaveBeenCalledWith({
|
||||
type: SO_SLO_TYPE,
|
||||
page: 1,
|
||||
perPage: 25,
|
||||
search: SOME_SLO.name,
|
||||
searchFields: ['name'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@ export interface SLORepository {
|
|||
findAllByIds(ids: string[]): Promise<SLO[]>;
|
||||
findById(id: string): Promise<SLO>;
|
||||
deleteById(id: string): Promise<void>;
|
||||
search(search: string): Promise<SLO[]>;
|
||||
}
|
||||
|
||||
export class KibanaSavedObjectsSLORepository implements SLORepository {
|
||||
|
@ -97,6 +98,21 @@ export class KibanaSavedObjectsSLORepository implements SLORepository {
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async search(search: string): Promise<SLO[]> {
|
||||
try {
|
||||
const response = await this.soClient.find<StoredSLO>({
|
||||
type: SO_SLO_TYPE,
|
||||
page: 1,
|
||||
perPage: 25,
|
||||
search,
|
||||
searchFields: ['name'],
|
||||
});
|
||||
return response.saved_objects.map((slo) => toSLO(slo.attributes));
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toStoredSLO(slo: SLO): StoredSLO {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue