[SLOs] Add/edit form show tags suggestions (#181075)

## Summary

Show tags as suggestions from existing SLOs

<img width="1194" alt="image"
src="12699ed3-ff26-436d-a63f-a3b9422a1cfc">
This commit is contained in:
Shahzad 2024-04-18 15:46:28 +02:00 committed by GitHub
parent ef5af17233
commit d3207ec1ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 130 additions and 2 deletions

View file

@ -0,0 +1,22 @@
/*
* 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 * as t from 'io-ts';
const getSLOSuggestionsResponseSchema = t.type({
tags: t.array(
t.type({
label: t.string,
value: t.string,
count: t.number,
})
),
});
type GetSLOSuggestionsResponse = t.OutputOf<typeof getSLOSuggestionsResponseSchema>;
export { getSLOSuggestionsResponseSchema };
export type { GetSLOSuggestionsResponse };

View file

@ -20,3 +20,4 @@ export * from './manage';
export * from './delete_instance';
export * from './fetch_historical_summary';
export * from './put_settings';
export * from './get_suggestions';

View file

@ -19,6 +19,7 @@ import {
import { i18n } from '@kbn/i18n';
import React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { useFetchSLOSuggestions } from '../hooks/use_fetch_suggestions';
import { OptionalText } from './common/optional_text';
import { CreateSLOForm } from '../types';
import { maxWidth } from './slo_edit_form';
@ -29,6 +30,8 @@ export function SloEditFormDescriptionSection() {
const descriptionId = useGeneratedHtmlId({ prefix: 'sloDescription' });
const tagsId = useGeneratedHtmlId({ prefix: 'tags' });
const { suggestions } = useFetchSLOSuggestions();
return (
<EuiPanel
hasBorder={false}
@ -121,8 +124,7 @@ export function SloEditFormDescriptionSection() {
defaultMessage: 'Add tags',
})}
isInvalid={fieldState.invalid}
options={[]}
noSuggestions
options={suggestions?.tags ?? []}
selectedOptions={generateTagOptions(field.value)}
onChange={(selected: EuiComboBoxOptionOption[]) => {
if (selected.length) {

View file

@ -0,0 +1,39 @@
/*
* 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 { useQuery } from '@tanstack/react-query';
import { GetSLOSuggestionsResponse } from '@kbn/slo-schema';
import { useKibana } from '../../../utils/kibana_react';
export function useFetchSLOSuggestions() {
const { http } = useKibana().services;
const { isLoading, isError, isSuccess, data } = useQuery({
queryKey: ['fetchSLOSuggestions'],
queryFn: async ({ signal }) => {
try {
return await http.get<GetSLOSuggestionsResponse>(
'/internal/api/observability/slos/suggestions',
{
signal,
}
);
} catch (error) {
// ignore error
}
},
refetchOnWindowFocus: false,
keepPreviousData: true,
});
return {
suggestions: data,
isLoading,
isSuccess,
isError,
};
}

View file

@ -24,6 +24,7 @@ import {
resetSLOParamsSchema,
updateSLOParamsSchema,
} from '@kbn/slo-schema';
import { GetSLOSuggestions } from '../../services/get_slo_suggestions';
import type { IndicatorTypes } from '../../domain/models';
import {
CreateSLO,
@ -445,6 +446,21 @@ const findSLOGroupsRoute = createSloServerRoute({
},
});
const getSLOSuggestionsRoute = createSloServerRoute({
endpoint: 'GET /internal/api/observability/slos/suggestions',
options: {
tags: ['access:slo_read'],
access: 'internal',
},
handler: async ({ context }) => {
await assertPlatinumLicense(context);
const soClient = (await context.core).savedObjects.client;
const getSLOSuggestions = new GetSLOSuggestions(soClient);
return await getSLOSuggestions.execute();
},
});
const deleteSloInstancesRoute = createSloServerRoute({
endpoint: 'POST /api/observability/slos/_delete_instances 2023-10-31',
options: {
@ -642,4 +658,5 @@ export const sloRouteRepository = {
...getSLOInstancesRoute,
...resetSLORoute,
...findSLOGroupsRoute,
...getSLOSuggestionsRoute,
};

View file

@ -0,0 +1,47 @@
/*
* 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 { SavedObjectsClientContract } from '@kbn/core/server';
import { GetSLOSuggestionsResponse } from '@kbn/slo-schema';
import { SO_SLO_TYPE } from '../saved_objects';
type Buckets = Array<{
key: string;
doc_count: number;
}>;
interface AggsResponse {
tagsAggs: {
buckets: Buckets;
};
}
export class GetSLOSuggestions {
constructor(private soClient: SavedObjectsClientContract) {}
public async execute(): Promise<GetSLOSuggestionsResponse> {
const findResponse = await this.soClient.find({
type: SO_SLO_TYPE,
perPage: 0,
aggs: {
tagsAggs: {
terms: {
field: `${SO_SLO_TYPE}.attributes.tags`,
size: 10000,
},
},
},
});
const { tagsAggs } = (findResponse?.aggregations as AggsResponse) ?? {};
return {
tags:
tagsAggs?.buckets?.map(({ key, doc_count: count }) => ({
label: key,
value: key,
count,
})) ?? [],
};
}
}