diff --git a/x-pack/plugins/observability/common/index.ts b/x-pack/plugins/observability/common/index.ts index a8eb77cf34a48..f8f032f93bda7 100644 --- a/x-pack/plugins/observability/common/index.ts +++ b/x-pack/plugins/observability/common/index.ts @@ -74,6 +74,7 @@ export const ruleDetailsLocatorID = 'RULE_DETAILS_LOCATOR'; export const rulesLocatorID = 'RULES_LOCATOR'; export const sloDetailsLocatorID = 'SLO_DETAILS_LOCATOR'; export const sloEditLocatorID = 'SLO_EDIT_LOCATOR'; +export const sloListLocatorID = 'SLO_LIST_LOCATOR'; import { paths } from './locators/paths'; export const observabilityPaths = paths.observability; diff --git a/x-pack/plugins/observability/public/locators/slo_details.test.ts b/x-pack/plugins/observability/public/locators/slo_details.test.ts index 6dfd06661f57f..9c5440232457a 100644 --- a/x-pack/plugins/observability/public/locators/slo_details.test.ts +++ b/x-pack/plugins/observability/public/locators/slo_details.test.ts @@ -5,19 +5,35 @@ * 2.0. */ -import { SloDetailsLocatorDefinition, getSloDetailsPath } from './slo_details'; +import { SloDetailsLocatorDefinition } from './slo_details'; describe('SloDetailsLocator', () => { const locator = new SloDetailsLocatorDefinition(); - it('should return correct url when empty params are provided', async () => { + it('returns correct url when empty params are provided', async () => { const location = await locator.getLocation({}); expect(location.app).toEqual('observability'); - expect(location.path).toEqual(`${getSloDetailsPath('')}`); + expect(location.path).toEqual('/slos'); }); - it('should return correct url when sloId is provided', async () => { + it('returns correct url when sloId is provided', async () => { const location = await locator.getLocation({ sloId: 'foo' }); - expect(location.path).toEqual(`${getSloDetailsPath('foo')}`); + expect(location.path).toEqual('/slos/foo'); + }); + + it('returns correct url when sloId and instanceId are provided', async () => { + const location = await locator.getLocation({ + sloId: 'some-slo_id', + instanceId: 'another-instance_id', + }); + expect(location.path).toEqual('/slos/some-slo_id?instanceId=another-instance_id'); + }); + + it("returns correct url when sloId and instanceId='*' is provided", async () => { + const location = await locator.getLocation({ + sloId: 'some-slo_id', + instanceId: '*', + }); + expect(location.path).toEqual('/slos/some-slo_id'); }); }); diff --git a/x-pack/plugins/observability/public/locators/slo_details.ts b/x-pack/plugins/observability/public/locators/slo_details.ts index 913c848508b61..95d003e94a1f5 100644 --- a/x-pack/plugins/observability/public/locators/slo_details.ts +++ b/x-pack/plugins/observability/public/locators/slo_details.ts @@ -10,23 +10,22 @@ import type { LocatorDefinition } from '@kbn/share-plugin/public'; import { sloDetailsLocatorID } from '../../common'; import { SLOS_PATH } from '../../common/locators/paths'; -export interface SloDetailsParams { +export interface SloDetailsLocatorParams extends SerializableRecord { sloId?: string; + instanceId?: string; } -export interface SloDetailsLocatorParams extends SloDetailsParams, SerializableRecord {} - -export const getSloDetailsPath = (sloId: string) => { - return `/${SLOS_PATH}/${encodeURI(sloId)}`; -}; - export class SloDetailsLocatorDefinition implements LocatorDefinition { public readonly id = sloDetailsLocatorID; - public readonly getLocation = async ({ sloId = '' }: SloDetailsLocatorParams) => { + public readonly getLocation = async ({ sloId, instanceId }: SloDetailsLocatorParams) => { + const queryParams = + !!instanceId && instanceId !== '*' ? `?instanceId=${encodeURI(instanceId)}` : ''; + const path = !!sloId ? `${SLOS_PATH}/${encodeURI(sloId)}${queryParams}` : SLOS_PATH; + return { app: 'observability', - path: getSloDetailsPath(sloId), + path, state: {}, }; }; diff --git a/x-pack/plugins/observability/public/locators/slo_list.test.ts b/x-pack/plugins/observability/public/locators/slo_list.test.ts new file mode 100644 index 0000000000000..f12bab21f6dd4 --- /dev/null +++ b/x-pack/plugins/observability/public/locators/slo_list.test.ts @@ -0,0 +1,30 @@ +/* + * 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 { SloListLocatorDefinition } from './slo_list'; + +describe('SloListLocator', () => { + const locator = new SloListLocatorDefinition(); + + it("returns the correct url with the default search state when no 'kqlQuery' provided", async () => { + const location = await locator.getLocation({}); + expect(location.app).toEqual('observability'); + expect(location.path).toEqual( + "/slos?search=(kqlQuery:'',page:0,sort:(by:status,direction:desc))" + ); + }); + + it("returns the correct url with the 'kqlQuery' provided", async () => { + const location = await locator.getLocation({ + kqlQuery: 'slo.name: "Service Availability" and slo.indicator.type : "sli.kql.custom"', + }); + expect(location.app).toEqual('observability'); + expect(location.path).toEqual( + "/slos?search=(kqlQuery:'slo.name:%20%22Service%20Availability%22%20and%20slo.indicator.type%20:%20%22sli.kql.custom%22',page:0,sort:(by:status,direction:desc))" + ); + }); +}); diff --git a/x-pack/plugins/observability/public/locators/slo_list.ts b/x-pack/plugins/observability/public/locators/slo_list.ts new file mode 100644 index 0000000000000..49acf4154afdc --- /dev/null +++ b/x-pack/plugins/observability/public/locators/slo_list.ts @@ -0,0 +1,44 @@ +/* + * 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 { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; +import type { LocatorDefinition } from '@kbn/share-plugin/public'; +import type { SerializableRecord } from '@kbn/utility-types'; +import deepmerge from 'deepmerge'; +import { sloListLocatorID } from '../../common'; +import { SLOS_PATH } from '../../common/locators/paths'; +import { + DEFAULT_STATE, + SearchState, + SLO_LIST_SEARCH_URL_STORAGE_KEY, +} from '../pages/slos/hooks/use_url_search_state'; + +export interface SloListLocatorParams extends SerializableRecord { + kqlQuery?: string; +} + +export class SloListLocatorDefinition implements LocatorDefinition { + public readonly id = sloListLocatorID; + + public readonly getLocation = async ({ kqlQuery = '' }: SloListLocatorParams) => { + const state: SearchState = deepmerge(DEFAULT_STATE, { kqlQuery }); + + return { + app: 'observability', + path: setStateToKbnUrl( + SLO_LIST_SEARCH_URL_STORAGE_KEY, + state, + { + useHash: false, + storeInHashQuery: false, + }, + SLOS_PATH + ), + state: {}, + }; + }; +} diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts index 2d7fb860900fa..7a0c03215fb91 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts @@ -8,7 +8,9 @@ import { useHistory } from 'react-router-dom'; import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import deepmerge from 'deepmerge'; -import { SortField } from '../components/slo_list_search_bar'; +import type { SortField } from '../components/slo_list_search_bar'; + +export const SLO_LIST_SEARCH_URL_STORAGE_KEY = 'search'; export interface SearchState { kqlQuery: string; @@ -36,11 +38,14 @@ export function useUrlSearchState(): { useHashQuery: false, }); - const searchState = urlStateStorage.get('search') ?? DEFAULT_STATE; + const searchState = + urlStateStorage.get(SLO_LIST_SEARCH_URL_STORAGE_KEY) ?? DEFAULT_STATE; return { state: deepmerge(DEFAULT_STATE, searchState), store: (state: Partial) => - urlStateStorage.set('search', deepmerge(searchState, state), { replace: true }), + urlStateStorage.set(SLO_LIST_SEARCH_URL_STORAGE_KEY, deepmerge(searchState, state), { + replace: true, + }), }; } diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 88191351f5444..87fc2a2a94365 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -5,11 +5,10 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; -import { BehaviorSubject, from } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; -import { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; +import { CasesDeepLinkId, CasesUiStart, getCasesDeepLinks } from '@kbn/cases-plugin/public'; +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { CloudStart } from '@kbn/cloud-plugin/public'; +import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { AppDeepLink, AppMountParameters, @@ -22,54 +21,44 @@ import { PluginInitializerContext, } from '@kbn/core/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { DiscoverStart } from '@kbn/discover-plugin/public'; import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { HomePublicPluginSetup, HomePublicPluginStart } from '@kbn/home-plugin/public'; -import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; -import type { CloudStart } from '@kbn/cloud-plugin/public'; +import { i18n } from '@kbn/i18n'; +import type { LensPublicStart } from '@kbn/lens-plugin/public'; import type { + NavigationEntry, ObservabilitySharedPluginSetup, ObservabilitySharedPluginStart, - NavigationEntry, } from '@kbn/observability-shared-plugin/public'; -import { CasesDeepLinkId, CasesUiStart, getCasesDeepLinks } from '@kbn/cases-plugin/public'; -import type { LensPublicStart } from '@kbn/lens-plugin/public'; +import { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; -import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import { BehaviorSubject, from } from 'rxjs'; +import { map } from 'rxjs/operators'; -import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import { - ActionTypeRegistryContract, - RuleTypeRegistryContract, -} from '@kbn/triggers-actions-ui-plugin/public'; -import { SecurityPluginStart } from '@kbn/security-plugin/public'; +import { AiopsPluginStart } from '@kbn/aiops-plugin/public/types'; +import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; +import { ExploratoryViewPublicStart } from '@kbn/exploratory-view-plugin/public'; import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; -import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; -import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import { ExploratoryViewPublicStart } from '@kbn/exploratory-view-plugin/public'; import { ObservabilityAIAssistantPluginSetup, ObservabilityAIAssistantPluginStart, } from '@kbn/observability-ai-assistant-plugin/public'; -import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; -import { AiopsPluginStart } from '@kbn/aiops-plugin/public/types'; -import { RulesLocatorDefinition } from './locators/rules'; -import { RuleDetailsLocatorDefinition } from './locators/rule_details'; -import { SloDetailsLocatorDefinition } from './locators/slo_details'; -import { SloEditLocatorDefinition } from './locators/slo_edit'; -import { observabilityAppId, observabilityFeatureId } from '../common'; -import { registerDataHandler } from './context/has_data_context/data_handler'; +import { SecurityPluginStart } from '@kbn/security-plugin/public'; +import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { - createObservabilityRuleTypeRegistry, - ObservabilityRuleTypeRegistry, -} from './rules/create_observability_rule_type_registry'; -import { registerObservabilityRuleTypes } from './rules/register_observability_rule_types'; -import { createUseRulesLink } from './hooks/create_use_rules_link'; + ActionTypeRegistryContract, + RuleTypeRegistryContract, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; +import { observabilityAppId, observabilityFeatureId } from '../common'; import { ALERTS_PATH, CASES_PATH, @@ -78,6 +67,18 @@ import { RULES_PATH, SLOS_PATH, } from '../common/locators/paths'; +import { registerDataHandler } from './context/has_data_context/data_handler'; +import { createUseRulesLink } from './hooks/create_use_rules_link'; +import { RulesLocatorDefinition } from './locators/rules'; +import { RuleDetailsLocatorDefinition } from './locators/rule_details'; +import { SloDetailsLocatorDefinition } from './locators/slo_details'; +import { SloEditLocatorDefinition } from './locators/slo_edit'; +import { SloListLocatorDefinition } from './locators/slo_list'; +import { + createObservabilityRuleTypeRegistry, + ObservabilityRuleTypeRegistry, +} from './rules/create_observability_rule_type_registry'; +import { registerObservabilityRuleTypes } from './rules/register_observability_rule_types'; export interface ConfigSchema { unsafe: { @@ -231,8 +232,8 @@ export class Plugin const sloDetailsLocator = pluginsSetup.share.url.locators.create( new SloDetailsLocatorDefinition() ); - const sloEditLocator = pluginsSetup.share.url.locators.create(new SloEditLocatorDefinition()); + const sloListLocator = pluginsSetup.share.url.locators.create(new SloListLocatorDefinition()); const mount = async (params: AppMountParameters) => { // Load application bundle @@ -372,6 +373,7 @@ export class Plugin ruleDetailsLocator, sloDetailsLocator, sloEditLocator, + sloListLocator, }; }