Skip to content

Commit

Permalink
feat(slo): add slo list page locator (elastic#169453)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdelemme authored Oct 24, 2023
1 parent 2def5ef commit 60a4f70
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 51 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/observability/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 21 additions & 5 deletions x-pack/plugins/observability/public/locators/slo_details.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
17 changes: 8 additions & 9 deletions x-pack/plugins/observability/public/locators/slo_details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<SloDetailsLocatorParams> {
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: {},
};
};
Expand Down
30 changes: 30 additions & 0 deletions x-pack/plugins/observability/public/locators/slo_list.test.ts
Original file line number Diff line number Diff line change
@@ -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))"
);
});
});
44 changes: 44 additions & 0 deletions x-pack/plugins/observability/public/locators/slo_list.ts
Original file line number Diff line number Diff line change
@@ -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<SloListLocatorParams> {
public readonly id = sloListLocatorID;

public readonly getLocation = async ({ kqlQuery = '' }: SloListLocatorParams) => {
const state: SearchState = deepmerge<SearchState>(DEFAULT_STATE, { kqlQuery });

return {
app: 'observability',
path: setStateToKbnUrl(
SLO_LIST_SEARCH_URL_STORAGE_KEY,
state,
{
useHash: false,
storeInHashQuery: false,
},
SLOS_PATH
),
state: {},
};
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -36,11 +38,14 @@ export function useUrlSearchState(): {
useHashQuery: false,
});

const searchState = urlStateStorage.get<SearchState>('search') ?? DEFAULT_STATE;
const searchState =
urlStateStorage.get<SearchState>(SLO_LIST_SEARCH_URL_STORAGE_KEY) ?? DEFAULT_STATE;

return {
state: deepmerge(DEFAULT_STATE, searchState),
store: (state: Partial<SearchState>) =>
urlStateStorage.set('search', deepmerge(searchState, state), { replace: true }),
urlStateStorage.set(SLO_LIST_SEARCH_URL_STORAGE_KEY, deepmerge(searchState, state), {
replace: true,
}),
};
}
70 changes: 36 additions & 34 deletions x-pack/plugins/observability/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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: {
Expand Down Expand Up @@ -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<unknown>) => {
// Load application bundle
Expand Down Expand Up @@ -372,6 +373,7 @@ export class Plugin
ruleDetailsLocator,
sloDetailsLocator,
sloEditLocator,
sloListLocator,
};
}

Expand Down

0 comments on commit 60a4f70

Please sign in to comment.