From 9851a1547721b8d7104384cb5c7ebb21c0510681 Mon Sep 17 00:00:00 2001 From: Milton Hultgren Date: Wed, 17 Apr 2024 15:13:03 +0200 Subject: [PATCH 01/36] [Obs AI Assistnat] Add tests for withTokenBudget (#181034) --- .../common/utils/with_token_budget.test.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 x-pack/plugins/observability_solution/observability_ai_assistant/common/utils/with_token_budget.test.ts diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/common/utils/with_token_budget.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/common/utils/with_token_budget.test.ts new file mode 100644 index 0000000000000..9a3027bf07866 --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/common/utils/with_token_budget.test.ts @@ -0,0 +1,53 @@ +/* + * 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 { withTokenBudget } from './with_token_budget'; + +describe('withTokenBudget', () => { + describe('when applying budgets', () => { + it('returns only the items that fit in the budget', () => { + const items = ['Some content', 'Another piece of content', 'And still more content']; // [2, 4, 4] tokens + expect(withTokenBudget(items, 6)).toEqual(items.slice(0, 2)); + }); + + it('returns all items if they all fit in the budget', () => { + const items = ['Some content', 'Another piece of content', 'And still more content']; // [2, 4, 4] tokens + expect(withTokenBudget(items, 12)).toEqual(items); + }); + + it('returns no items if the first item exceeds the budget', () => { + const items = ['Some content', 'Another piece of content', 'And still more content']; // [2, 4, 4] tokens + expect(withTokenBudget(items, 1)).toEqual([]); + }); + + it('returns as many items as possible when asked to maximize the budget', () => { + const items = ['Another piece of content', 'And still more content', 'Some content']; // [4, 4, 2] tokens + expect(withTokenBudget(items, 6)).toEqual(items.slice(0, 1)); + expect(withTokenBudget(items, 6, { maximizeBudget: true })).toEqual([items[0], items[2]]); + }); + }); + + describe('when using different content types and accessors', () => { + it('accepts plain strings and decides the cost based on that', () => { + const items = ['Some content']; // Worth 2 tokens + expect(withTokenBudget(items, 2)).toEqual(items); + }); + + it('accepts complex types and decides the cost based on a serialization of the whole item', () => { + const items = [{ message: 'Some content', role: 'user' }]; // Worth 10 tokens total, message worth 2 tokens + expect(withTokenBudget(items, 2)).toEqual([]); + expect(withTokenBudget(items, 10)).toEqual(items); + }); + + it('accepts using a custom content accessor when using complex types', () => { + const items = [{ message: 'Some content', role: 'user' }]; // Worth 10 tokens total, message worth 2 tokens + expect(withTokenBudget(items, 2)).toEqual([]); + const contentAccessor = (item: typeof items[0]) => item.message; + expect(withTokenBudget(items, 2, { contentAccessor })).toEqual(items); + }); + }); +}); From 8d9bb1ce8ae124975d84208ceab640889a41ac63 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 17 Apr 2024 15:22:45 +0200 Subject: [PATCH 02/36] [ES|QL] Some validation logic improvements (#180625) ## Summary Just some refactoring to make function arg checking more clear. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli --- .../src/shared/helpers.ts | 5 +- .../src/validation/helpers.ts | 43 +++++++++- .../src/validation/validation.ts | 83 +++++++------------ 3 files changed, 75 insertions(+), 56 deletions(-) diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts index 1d439aa3a0b9e..cf5ed3304b41b 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts @@ -233,7 +233,10 @@ export function isArrayType(type: string) { return ARRAY_REGEXP.test(type); } -export function extractSingleType(type: string) { +/** + * Given an array type for example `string[]` it will return `string` + */ +export function extractSingularType(type: string) { return type.replace(ARRAY_REGEXP, ''); } diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts index a60d705d0d0d3..9abb3ea3dc2aa 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import type { ESQLAst } from '@kbn/esql-ast'; -import type { ESQLPolicy } from './types'; +import type { ESQLAst, ESQLAstItem, ESQLMessage, ESQLSingleAstItem } from '@kbn/esql-ast'; +import { getAllArrayTypes, getAllArrayValues } from '../shared/helpers'; +import { getMessageFromId } from './errors'; +import type { ESQLPolicy, ReferenceMaps } from './types'; export function buildQueryForFieldsFromSource(queryString: string, ast: ESQLAst) { const firstCommand = ast[0]; @@ -35,3 +37,40 @@ export function buildQueryForFieldsForStringSources(queryString: string, ast: ES } return customQuery; } + +/** + * We only want to report one message when any number of the elements in an array argument is of the wrong type + */ +export function collapseWrongArgumentTypeMessages( + messages: ESQLMessage[], + arg: ESQLAstItem[], + funcName: string, + argType: string, + parentCommand: string, + references: ReferenceMaps +) { + if (!messages.some(({ code }) => code === 'wrongArgumentType')) { + return messages; + } + + // Replace the individual "wrong argument type" messages with a single one for the whole array + messages = messages.filter(({ code }) => code !== 'wrongArgumentType'); + + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: funcName, + argType, + value: `(${getAllArrayValues(arg).join(', ')})`, + givenType: `(${getAllArrayTypes(arg, parentCommand, references).join(', ')})`, + }, + locations: { + min: (arg[0] as ESQLSingleAstItem).location.min, + max: (arg[arg.length - 1] as ESQLSingleAstItem).location.max, + }, + }) + ); + + return messages; +} diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts index bfa5d6fc9cb64..af456e90d3f72 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts @@ -28,9 +28,7 @@ import { } from '../definitions/types'; import { areFieldAndVariableTypesCompatible, - extractSingleType, - getAllArrayTypes, - getAllArrayValues, + extractSingularType, getColumnHit, getCommandDefinition, getFunctionDefinition, @@ -73,6 +71,7 @@ import { retrieveMetadataFields, retrieveFieldsFromStringSources, } from './resources'; +import { collapseWrongArgumentTypeMessages } from './helpers'; function validateFunctionLiteralArg( astFunction: ESQLFunction, @@ -470,66 +469,44 @@ function validateFunction( // few lines above return; } - // if the arg is an array of values, check each element - if (Array.isArray(outerArg) && isArrayType(argDef.type)) { - const extractedType = extractSingleType(argDef.type); - const everyArgInListMessages = outerArg - .map((arg) => { - return [ - validateFunctionLiteralArg, - validateNestedFunctionArg, - validateFunctionColumnArg, - ].flatMap((validateFn) => { - return validateFn( - astFunction, - arg, - { - ...argDef, - constantOnly: forceConstantOnly || argDef.constantOnly, - type: extractedType, - }, - references, - parentCommand - ); - }); - }) - .filter((ms) => ms.length); - if (everyArgInListMessages.length) { - failingSignature.push( - getMessageFromId({ - messageId: 'wrongArgumentType', - values: { - name: astFunction.name, - argType: argDef.type, - value: `(${getAllArrayValues(outerArg).join(', ')})`, - givenType: `(${getAllArrayTypes(outerArg, parentCommand, references).join(', ')})`, - }, - locations: { - min: (outerArg[0] as ESQLSingleAstItem).location.min, - max: (outerArg[outerArg.length - 1] as ESQLSingleAstItem).location.max, - }, - }) - ); - } - return; - } - const wrappedArg = Array.isArray(outerArg) ? outerArg : [outerArg]; - for (const actualArg of wrappedArg) { - const argValidationMessages = [ + + // check every element of the argument (may be an array of elements, or may be a single element) + const hasMultipleElements = Array.isArray(outerArg); + const argElements = hasMultipleElements ? outerArg : [outerArg]; + const singularType = extractSingularType(argDef.type); + const messagesFromAllArgElements = argElements.flatMap((arg) => { + return [ validateFunctionLiteralArg, validateNestedFunctionArg, validateFunctionColumnArg, ].flatMap((validateFn) => { return validateFn( astFunction, - actualArg, - { ...argDef, constantOnly: forceConstantOnly || argDef.constantOnly }, + arg, + { + ...argDef, + type: singularType, + constantOnly: forceConstantOnly || argDef.constantOnly, + }, references, parentCommand ); }); - failingSignature.push(...argValidationMessages); - } + }); + + const shouldCollapseMessages = isArrayType(argDef.type) && hasMultipleElements; + failingSignature.push( + ...(shouldCollapseMessages + ? collapseWrongArgumentTypeMessages( + messagesFromAllArgElements, + outerArg, + astFunction.name, + argDef.type, + parentCommand, + references + ) + : messagesFromAllArgElements) + ); }); if (failingSignature.length) { failingSignatures.push(failingSignature); From d5db103fae34d775036257cf9ccd31bdc13fddb2 Mon Sep 17 00:00:00 2001 From: Sid Date: Wed, 17 Apr 2024 15:26:31 +0200 Subject: [PATCH 03/36] Update roles management to handle creating, editing and listing custom roles in serverless (#179334) Closes https://github.com/elastic/kibana/issues/178913 Closes https://github.com/elastic/kibana/issues/178906 ## Summary Updates the existing roles listing page to handle listing custom roles for serverless. Updates the create/edit roles page to handle serverless custom roles. https://github.com/elastic/kibana/assets/1442420/10e2e779-2717-4802-932a-d9943a5cc937 ## Testing Following testing strategy laid out in: https://github.com/elastic/kibana/pull/176761 - set up as follows 1. In the `kibana.dev.yml` file, add the following settings. This enables the role management feature flag, and provides cloud URLs for the `Manage organization members` card. ``` xpack.security.roleManagementEnabled: true xpack.cloud.base_url: 'https://cloud.elastic.co' xpack.cloud.organization_url: '/account/members' ``` 2. Start Elasticsearch and Kibana in serverless mode and SSL enabled (to access the test user selector) as follows: ``` yarn es --serverless=es --ssl -E xpack.security.authc.native_roles.enabled=true yarn start --serverless=es --ssl ``` 3. Navigate to Kibana (use https as SSL is enabled), and log in as the elastic_serverless user or as Admin test user. 4. Navigate to the Management page using the side navigation bar. Verify the three new cards are rendered in a new Access section. 5. Using the custom roles card, navigate to the Custom Roles link. 6. Verify the following a. The page shows the title and description as described above b. No reserved roles should be displayed c. An external link to the cloud console of your project to assign roles which navigates to the config above `xpack.cloud.base_url+xpack.cloud.organization_url` 7. Clicking on create role should take you to the create/edit roles page 8. Create a new role and assign it the privileges that you'd like 9. On clicking create role, you should be taken back to the role listing page and your new role should be displayed here along with a toast with a success message 10. On trying to delete the role, you will see a delete confirmation modal. On clicking delete, you should see the role removed from the role listing page along with a toast message. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../edit_role/delete_role_button.test.tsx | 12 +- .../roles/edit_role/delete_role_button.tsx | 36 +++- .../roles/edit_role/edit_role_page.test.tsx | 80 ++++++++- .../roles/edit_role/edit_role_page.tsx | 164 +++++++++++++++--- .../elasticsearch_privileges.test.tsx.snap | 156 +++++++++++++++++ .../es/elasticsearch_privileges.test.tsx | 11 ++ .../es/elasticsearch_privileges.tsx | 95 +++++----- .../confirm_delete/confirm_delete.tsx | 113 +++++++++--- .../roles/roles_grid/roles_grid_page.tsx | 155 +++++++++++------ .../roles/roles_management_app.test.tsx | 162 ++++++++++++++++- .../management/roles/roles_management_app.tsx | 11 +- .../routes/authorization/roles/get_all.ts | 6 + x-pack/plugins/security/tsconfig.json | 3 + 13 files changed, 840 insertions(+), 164 deletions(-) diff --git a/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.test.tsx index 32d1e4fa6bafb..0b2625ee747ac 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.test.tsx @@ -14,14 +14,18 @@ import { DeleteRoleButton } from './delete_role_button'; test('it renders without crashing', () => { const deleteHandler = jest.fn(); - const wrapper = shallowWithIntl(); + const wrapper = shallowWithIntl( + + ); expect(wrapper.find(EuiButtonEmpty)).toHaveLength(1); expect(deleteHandler).toHaveBeenCalledTimes(0); }); test('it shows a confirmation dialog when clicked', () => { const deleteHandler = jest.fn(); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl( + + ); wrapper.find(EuiButtonEmpty).simulate('click'); @@ -32,7 +36,9 @@ test('it shows a confirmation dialog when clicked', () => { test('it renders nothing when canDelete is false', () => { const deleteHandler = jest.fn(); - const wrapper = shallowWithIntl(); + const wrapper = shallowWithIntl( + + ); expect(wrapper.find('*')).toHaveLength(0); expect(deleteHandler).toHaveBeenCalledTimes(0); }); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.tsx b/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.tsx index 5ffe04f6e4a58..1057c17f56206 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.tsx @@ -8,11 +8,14 @@ import { EuiButtonEmpty, EuiConfirmModal } from '@elastic/eui'; import React, { Component, Fragment } from 'react'; +import type { BuildFlavor } from '@kbn/config'; import { FormattedMessage } from '@kbn/i18n-react'; interface Props { + roleName: string; canDelete: boolean; onDelete: () => void; + buildFlavor?: BuildFlavor; } interface State { @@ -43,16 +46,25 @@ export class DeleteRoleButton extends Component { } public maybeShowModal = () => { + const { buildFlavor, roleName } = this.props; if (!this.state.showModal) { return null; } return ( + buildFlavor !== 'serverless' ? ( + + ) : ( + + ) } onCancel={this.closeModal} onConfirm={this.onConfirmDelete} @@ -71,10 +83,18 @@ export class DeleteRoleButton extends Component { buttonColor={'danger'} >

- + {buildFlavor !== 'serverless' ? ( + + ) : ( + + )}

', () => { expectSaveFormButtons(wrapper); }); + it('can render for serverless buildFlavor', async () => { + const dataViews = dataViewPluginMocks.createStartContract(); + dataViews.getTitles = jest.fn().mockRejectedValue({ response: { status: 403 } }); + + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0); + expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1); + expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); + expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('ElasticsearchPrivileges').prop('buildFlavor')).toBe('serverless'); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(0); + expectSaveFormButtons(wrapper); + }); + describe('in create mode', () => { it('renders an error for existing role name', async () => { const props = getProps({ action: 'edit' }); @@ -779,6 +825,38 @@ describe('', () => { expectSaveFormButtons(wrapper); }); + it('can render for serverless buildFlavor', async () => { + const props = getProps({ action: 'edit', buildFlavor: 'serverless' }); + const wrapper = mountWithIntl( + + + + ); + + props.rolesAPIClient.getRole.mockRejectedValue(new Error('not found')); + + await waitForRender(wrapper); + + const nameInput = wrapper.find('input[name="name"]'); + nameInput.simulate('change', { target: { value: 'system_indices_superuser' } }); + nameInput.simulate('blur'); + + await waitForRender(wrapper); + + expect(wrapper.find('EuiFormRow[data-test-subj="roleNameFormRow"]').props()).toMatchObject({ + isInvalid: false, + }); + expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0); + expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); + expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe( + false + ); + expect(wrapper.find('ElasticsearchPrivileges').prop('buildFlavor')).toBe('serverless'); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(0); + expectSaveFormButtons(wrapper); + }); + it('does not render a notification on save of new role name', async () => { const props = getProps({ action: 'edit' }); const wrapper = mountWithIntl( diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 293b90259b2ab..8e0cb5f0cf065 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -21,15 +21,19 @@ import { } from '@elastic/eui'; import type { ChangeEvent, FocusEvent, FunctionComponent, HTMLProps } from 'react'; import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'; +import type { AsyncState } from 'react-use/lib/useAsync'; import useAsync from 'react-use/lib/useAsync'; +import type { BuildFlavor } from '@kbn/config'; import type { Capabilities, DocLinksStart, FatalErrorsSetup, HttpStart, + I18nStart, NotificationsStart, ScopedHistory, + ThemeServiceStart, } from '@kbn/core/public'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; @@ -38,6 +42,7 @@ import type { FeaturesPluginStart } from '@kbn/features-plugin/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { reactRouterNavigate, useDarkMode } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import type { Cluster } from '@kbn/remote-clusters-plugin/public'; import { REMOTE_CLUSTERS_PATH } from '@kbn/remote-clusters-plugin/public'; import type { Space, SpacesApiUi } from '@kbn/spaces-plugin/public'; @@ -88,29 +93,56 @@ interface Props { fatalErrors: FatalErrorsSetup; history: ScopedHistory; spacesApiUi?: SpacesApiUi; + buildFlavor: BuildFlavor; + i18nStart: I18nStart; + theme: ThemeServiceStart; + cloudOrgUrl?: string; } function useRemoteClusters(http: HttpStart) { return useAsync(() => http.get(REMOTE_CLUSTERS_PATH)); } -function useFeatureCheck(http: HttpStart) { - return useAsync(() => - http.get('/internal/security/_check_role_mapping_features') - ); +interface CheckRoleMappingFeaturesResponseWhenServerless { + value: boolean; +} +function useFeatureCheck( + http: HttpStart, + buildFlavor: 'serverless' +): AsyncState; + +function useFeatureCheck( + http: HttpStart, + buildFlavor: BuildFlavor +): AsyncState; + +function useFeatureCheck(http: HttpStart, buildFlavor?: BuildFlavor) { + return useAsync(async () => { + if (buildFlavor !== 'serverless') { + return http.get( + '/internal/security/_check_role_mapping_features' + ); + } + return { value: true }; + }, [http, buildFlavor]); } function useRunAsUsers( userAPIClient: PublicMethodsOf, - fatalErrors: FatalErrorsSetup + fatalErrors: FatalErrorsSetup, + buildFlavor: BuildFlavor ) { const [userNames, setUserNames] = useState(null); useEffect(() => { - userAPIClient.getUsers().then( - (users) => setUserNames(users.map((user) => user.username)), - (err) => fatalErrors.add(err) - ); - }, [fatalErrors, userAPIClient]); + if (buildFlavor !== 'serverless') { + userAPIClient.getUsers().then( + (users) => setUserNames(users.map((user) => user.username)), + (err) => fatalErrors.add(err) + ); + } else { + setUserNames([]); + } + }, [fatalErrors, userAPIClient, buildFlavor]); return userNames; } @@ -303,6 +335,10 @@ export const EditRolePage: FunctionComponent = ({ notifications, history, spacesApiUi, + buildFlavor, + i18nStart, + theme, + cloudOrgUrl, }) => { const isDarkMode = useDarkMode(); @@ -322,12 +358,12 @@ export const EditRolePage: FunctionComponent = ({ const [formError, setFormError] = useState(null); const [creatingRoleAlreadyExists, setCreatingRoleAlreadyExists] = useState(false); const [previousName, setPreviousName] = useState(''); - const runAsUsers = useRunAsUsers(userAPIClient, fatalErrors); + const runAsUsers = useRunAsUsers(userAPIClient, fatalErrors, buildFlavor); const indexPatternsTitles = useIndexPatternsTitles(dataViews, fatalErrors, notifications); const privileges = usePrivileges(privilegesAPIClient, fatalErrors); const spaces = useSpaces(http, fatalErrors); const features = useFeatures(getFeatures, fatalErrors); - const featureCheckState = useFeatureCheck(http); + const featureCheckState = useFeatureCheck(http, buildFlavor); const remoteClustersState = useRemoteClusters(http); const [role, setRole] = useRole( rolesAPIClient, @@ -407,7 +443,12 @@ export const EditRolePage: FunctionComponent = ({ if (isEditingExistingRole && !isRoleReadOnly) { return ( - + ); } @@ -427,7 +468,12 @@ export const EditRolePage: FunctionComponent = ({ /> } helpText={ - !isRoleReserved && isEditingExistingRole ? ( + !isEditingExistingRole ? ( + + ) : !isRoleReserved ? ( = ({ builtinESPrivileges={builtInESPrivileges} license={license} docLinks={docLinks} - canUseRemoteIndices={featureCheckState.value?.canUseRemoteIndices} + canUseRemoteIndices={ + buildFlavor === 'traditional' && featureCheckState.value?.canUseRemoteIndices + } isDarkMode={isDarkMode} + buildFlavor={buildFlavor} /> ); @@ -605,12 +654,39 @@ export const EditRolePage: FunctionComponent = ({ return; } - notifications.toasts.addSuccess( - i18n.translate( - 'xpack.security.management.editRole.roleSuccessfullySavedNotificationMessage', - { defaultMessage: 'Saved role' } - ) - ); + if (buildFlavor === 'serverless') { + notifications.toasts.addSuccess({ + title: i18n.translate( + 'xpack.security.management.editRole.customRoleSuccessfullySavedNotificationTitle', + { defaultMessage: 'Custom role created' } + ), + text: toMountPoint( + <> +

+ +

+ + + + Assign role + + + + , + { i18n: i18nStart, theme } + ), + }); + } else { + notifications.toasts.addSuccess( + i18n.translate( + 'xpack.security.management.editRole.roleSuccessfullySavedNotificationMessage', + { defaultMessage: 'Saved role' } + ) + ); + } backToRoleList(); } @@ -638,12 +714,44 @@ export const EditRolePage: FunctionComponent = ({ return; } - notifications.toasts.addSuccess( - i18n.translate( - 'xpack.security.management.editRole.roleSuccessfullyDeletedNotificationMessage', - { defaultMessage: 'Deleted role' } - ) - ); + if (buildFlavor === 'serverless') { + notifications.toasts.addDanger({ + title: i18n.translate('xpack.security.management.roles.deleteRolesSuccessTitle', { + defaultMessage: + '{numberOfCustomRoles, plural, one {# custom role} other {# custom roles}} deleted', + values: { numberOfCustomRoles: 1 }, + }), + text: toMountPoint( + <> +

+ {i18n.translate('xpack.security.management.roles.deleteRolesSuccessMessage', { + defaultMessage: `The deleted role will still appear listed on the user profile in Organization + Management and on the User Profile for those that don't have admin access.`, + })} +

+ + + + + Manage Members + + + + , + { + i18n: i18nStart, + theme, + } + ), + }); + } else { + notifications.toasts.addSuccess( + i18n.translate( + 'xpack.security.management.editRole.roleSuccessfullyDeletedNotificationMessage', + { defaultMessage: 'Deleted role' } + ) + ); + } backToRoleList(); }; @@ -656,7 +764,7 @@ export const EditRolePage: FunctionComponent = ({ {isRoleReserved && ( diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap index 056e42ef2cc17..74013db24d9e4 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap @@ -1,5 +1,161 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`it renders correctly in serverless mode 1`] = ` + + + + + + +

+ } + title={ +

+ +

+ } + > + + + +
+ + +

+ +

+
+ + +

+ + + + +

+
+ +
+`; + exports[`it renders without crashing 1`] = ` { wrapper.find('IndexPrivileges').everyWhere((component) => component.prop('editable')) ).toBe(false); }); + +test('it renders correctly in serverless mode', () => { + expect( + shallowWithIntl( + // Enabled remote indices privilege to make sure remote indices is still not rendered due to build flavor + + ) + ).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx index 295324a9e531c..0aa4900e8715b 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import React, { Component, Fragment } from 'react'; +import type { BuildFlavor } from '@kbn/config'; import type { DocLinksStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -43,6 +44,7 @@ interface Props { remoteClusters?: Cluster[]; canUseRemoteIndices?: boolean; isDarkMode?: boolean; + buildFlavor: BuildFlavor; } export class ElasticsearchPrivileges extends Component { @@ -67,6 +69,7 @@ export class ElasticsearchPrivileges extends Component { license, builtinESPrivileges, canUseRemoteIndices, + buildFlavor, } = this.props; return ( @@ -99,52 +102,55 @@ export class ElasticsearchPrivileges extends Component { /> - - - - - } - description={ -

- + + + + } + description={ +

+ + {this.learnMore(docLinks.links.security.runAsPrivilege)} +

+ } + > + ({ + label: username, + isGroupLabelOption: false, + }))} + selectedOptions={this.props.role.elasticsearch.run_as.map((u) => ({ label: u }))} + onCreateOption={this.onCreateRunAsOption} + onChange={this.onRunAsUserChange} + isDisabled={!editable} /> - {this.learnMore(docLinks.links.security.runAsPrivilege)} -

- } - > - ({ - label: username, - isGroupLabelOption: false, - }))} - selectedOptions={this.props.role.elasticsearch.run_as.map((u) => ({ label: u }))} - onCreateOption={this.onCreateRunAsOption} - onChange={this.onRunAsUserChange} - isDisabled={!editable} - /> -
- + + + + )}

@@ -176,8 +182,7 @@ export class ElasticsearchPrivileges extends Component { editable={editable} isDarkMode={this.props.isDarkMode} /> - - {canUseRemoteIndices && ( + {buildFlavor === 'traditional' && canUseRemoteIndices && ( <> diff --git a/x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/confirm_delete.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/confirm_delete.tsx index 186f9bc4a4cb5..35aea7c9b99ca 100644 --- a/x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/confirm_delete.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/confirm_delete.tsx @@ -8,6 +8,8 @@ import { EuiButton, EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, EuiModal, EuiModalBody, EuiModalFooter, @@ -17,9 +19,12 @@ import { } from '@elastic/eui'; import React, { Component, Fragment } from 'react'; -import type { NotificationsStart } from '@kbn/core/public'; +import type { BuildFlavor } from '@kbn/config'; +import type { I18nStart, NotificationsStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { ThemeServiceStart } from '@kbn/react-kibana-context-common'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { RolesAPIClient } from '../../roles_api_client'; @@ -30,6 +35,10 @@ interface Props { onCancel: () => void; notifications: NotificationsStart; rolesAPIClient: PublicMethodsOf; + buildFlavor: BuildFlavor; + theme: ThemeServiceStart; + i18nStart: I18nStart; + cloudOrgUrl?: string; } interface State { @@ -45,10 +54,10 @@ export class ConfirmDelete extends Component { } public render() { - const { rolesToDelete } = this.props; + const { rolesToDelete, buildFlavor } = this.props; const moreThanOne = rolesToDelete.length > 1; const title = i18n.translate('xpack.security.management.roles.deleteRoleTitle', { - defaultMessage: 'Delete role{value, plural, one {{roleName}} other {s}}', + defaultMessage: `Delete role{value, plural, one {{roleName}} other {s}}?`, values: { value: rolesToDelete.length, roleName: ` ${rolesToDelete[0]}` }, }); @@ -64,25 +73,49 @@ export class ConfirmDelete extends Component { {moreThanOne ? ( -

- -

+ {buildFlavor === 'traditional' && ( +

+ +

+ )} + {buildFlavor === 'serverless' && ( +

+ +

+ )}
    {rolesToDelete.map((roleName) => (
  • {roleName}
  • ))}
- ) : null} -

- -

+ ) : ( + + {buildFlavor === 'serverless' && ( +

+ +

+ )} +
+ )} + {buildFlavor === 'traditional' && ( +

+ +

+ )}
@@ -126,18 +159,20 @@ export class ConfirmDelete extends Component { }; private deleteRoles = async () => { - const { rolesToDelete, callback, rolesAPIClient, notifications } = this.props; + const { rolesToDelete, callback, rolesAPIClient, notifications, buildFlavor } = this.props; const errors: string[] = []; const deleteOperations = rolesToDelete.map((roleName) => { const deleteRoleTask = async () => { try { await rolesAPIClient.deleteRole(roleName); - notifications.toasts.addSuccess( - i18n.translate( - 'xpack.security.management.roles.confirmDelete.roleSuccessfullyDeletedNotificationMessage', - { defaultMessage: 'Deleted role {roleName}', values: { roleName } } - ) - ); + if (buildFlavor === 'traditional') { + notifications.toasts.addSuccess( + i18n.translate( + 'xpack.security.management.roles.confirmDelete.roleSuccessfullyDeletedNotificationMessage', + { defaultMessage: 'Deleted role {roleName}', values: { roleName } } + ) + ); + } } catch (e) { errors.push(roleName); notifications.toasts.addDanger( @@ -154,6 +189,38 @@ export class ConfirmDelete extends Component { await Promise.all(deleteOperations); + if (buildFlavor === 'serverless') { + this.props.notifications.toasts.addDanger({ + title: i18n.translate('xpack.security.management.roles.deleteRolesSuccessTitle', { + defaultMessage: + '{numberOfCustomRoles, plural, one {# custom role} other {# custom roles}} deleted', + values: { numberOfCustomRoles: deleteOperations.length }, + }), + text: toMountPoint( + <> +

+ {i18n.translate('xpack.security.management.roles.deleteRolesSuccessMessage', { + defaultMessage: `The deleted role will still appear listed on the user profile in Organization + Management and on the User Profile for those that don't have admin access.`, + })} +

+ + + + + Manage Members + + + + , + { + i18n: this.props.i18nStart, + theme: this.props.theme, + } + ), + }); + } + callback(rolesToDelete, errors); }; } diff --git a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx index 1da786c7e6b70..2dc2628d23030 100644 --- a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx @@ -22,10 +22,12 @@ import { import _ from 'lodash'; import React, { Component } from 'react'; -import type { NotificationsStart, ScopedHistory } from '@kbn/core/public'; +import type { BuildFlavor } from '@kbn/config'; +import type { I18nStart, NotificationsStart, ScopedHistory } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public'; +import type { ThemeServiceStart } from '@kbn/react-kibana-context-common'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { ConfirmDelete } from './confirm_delete'; @@ -47,6 +49,10 @@ interface Props { rolesAPIClient: PublicMethodsOf; history: ScopedHistory; readOnly?: boolean; + buildFlavor: BuildFlavor; + theme: ThemeServiceStart; + i18nStart: I18nStart; + cloudOrgUrl?: string; } interface State { @@ -57,6 +63,7 @@ interface State { showDeleteConfirmation: boolean; permissionDenied: boolean; includeReservedRoles: boolean; + isLoading: boolean; } const getRoleManagementHref = (action: 'edit' | 'clone', roleName?: string) => { @@ -78,6 +85,7 @@ export class RolesGridPage extends Component { showDeleteConfirmation: false, permissionDenied: false, includeReservedRoles: true, + isLoading: false, }; } @@ -92,40 +100,77 @@ export class RolesGridPage extends Component { } private getPageContent = () => { - const { roles } = this.state; + const { isLoading } = this.state; + + const customRolesEnabled = this.props.buildFlavor === 'serverless'; + + const rolesTitle = customRolesEnabled ? ( + + ) : ( + + ); + + const rolesDescription = customRolesEnabled ? ( + + ) : ( + + ); + + const emptyResultsMessage = customRolesEnabled ? ( + + ) : ( + + ); + const pageRightSideItems = [ + + + , + ]; + if (customRolesEnabled) { + pageRightSideItems.push( + + + + ); + } return ( <> - } - description={ - - } - rightSideItems={ - this.props.readOnly - ? undefined - : [ - - - , - ] - } + pageTitle={rolesTitle} + description={rolesDescription} + rightSideItems={this.props.readOnly ? undefined : pageRightSideItems} /> @@ -137,6 +182,10 @@ export class RolesGridPage extends Component { callback={this.handleDelete} notifications={this.props.notifications} rolesAPIClient={this.props.rolesAPIClient} + buildFlavor={this.props.buildFlavor} + theme={this.props.theme} + i18nStart={this.props.i18nStart} + cloudOrgUrl={this.props.cloudOrgUrl} /> ) : null} @@ -161,8 +210,9 @@ export class RolesGridPage extends Component { initialPageSize: 20, pageSizeOptions: [10, 20, 30, 50, 100], }} + message={emptyResultsMessage} items={this.state.visibleRoles} - loading={roles.length === 0} + loading={isLoading} search={{ toolsLeft: this.renderToolsLeft(), toolsRight: this.renderToolsRight(), @@ -220,7 +270,9 @@ export class RolesGridPage extends Component { ); }, }, - { + ]; + if (this.props.buildFlavor !== 'serverless') { + config.push({ field: 'metadata', name: i18n.translate('xpack.security.management.roles.statusColumnName', { defaultMessage: 'Status', @@ -229,8 +281,8 @@ export class RolesGridPage extends Component { render: (metadata: Role['metadata'], record: Role) => { return this.getRoleStatusBadges(record); }, - }, - ]; + }); + } if (!this.props.readOnly) { config.push({ @@ -365,7 +417,7 @@ export class RolesGridPage extends Component { const deprecated = isRoleDeprecated(role); const reserved = isRoleReserved(role); - const badges = []; + const badges: JSX.Element[] = []; if (!enabled) { badges.push(); } @@ -419,6 +471,7 @@ export class RolesGridPage extends Component { private async loadRoles() { try { + this.setState({ isLoading: true }); const roles = await this.props.rolesAPIClient.getRoles(); this.setState({ @@ -440,6 +493,8 @@ export class RolesGridPage extends Component { }) ); } + } finally { + this.setState({ isLoading: false }); } } @@ -467,19 +522,21 @@ export class RolesGridPage extends Component { } private renderToolsRight() { - return ( - - } - checked={this.state.includeReservedRoles} - onChange={this.onIncludeReservedRolesChange} - /> - ); + if (this.props.buildFlavor !== 'serverless') { + return ( + + } + checked={this.state.includeReservedRoles} + onChange={this.onIncludeReservedRolesChange} + /> + ); + } } private onCancelDelete = () => { this.setState({ showDeleteConfirmation: false }); diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx index d9b1478905495..19bdb6d41c2ad 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx @@ -8,6 +8,7 @@ import { act } from '@testing-library/react'; import { noop } from 'lodash'; +import type { BuildFlavor } from '@kbn/config'; import { coreMock, scopedHistoryMock, themeServiceMock } from '@kbn/core/public/mocks'; import { featuresPluginMock } from '@kbn/features-plugin/public/mocks'; import type { Unmount } from '@kbn/management-plugin/public/types'; @@ -25,7 +26,7 @@ jest.mock('./edit_role', () => ({ `Role Edit Page: ${JSON.stringify({ ...props, docLinks: props.docLinks ? {} : undefined })}`, })); -async function mountApp(basePath: string, pathname: string) { +async function mountApp(basePath: string, pathname: string, buildFlavor?: BuildFlavor) { const { fatalErrors } = coreMock.createSetup(); const container = document.createElement('div'); const setBreadcrumbs = jest.fn(); @@ -48,7 +49,7 @@ async function mountApp(basePath: string, pathname: string) { getStartServices: jest .fn() .mockResolvedValue([coreStart, { data: {}, features: featuresStart }]), - buildFlavor: 'traditional', + buildFlavor: buildFlavor ?? 'traditional', }) .mount({ basePath, @@ -93,7 +94,7 @@ describe('rolesManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Roles Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"readOnly":false} + Roles Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"readOnly":false,"buildFlavor":"traditional","i18nStart":{},"theme":{"theme$":{}}}
`); @@ -115,7 +116,7 @@ describe('rolesManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}} + Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}},"buildFlavor":"traditional","i18nStart":{},"theme":{"theme$":{}}}
`); @@ -142,7 +143,7 @@ describe('rolesManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}} + Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}},"buildFlavor":"traditional","i18nStart":{},"theme":{"theme$":{}}}
`); @@ -169,7 +170,7 @@ describe('rolesManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}} + Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}},"buildFlavor":"traditional","i18nStart":{},"theme":{"theme$":{}}}
`); @@ -196,3 +197,152 @@ describe('rolesManagementApp', () => { ]); }); }); + +describe('rolesManagementApp - serverless', () => { + it('create() returns proper management app descriptor', () => { + const { fatalErrors, getStartServices } = coreMock.createSetup(); + + expect( + rolesManagementApp.create({ + license: licenseMock.create(), + fatalErrors, + getStartServices: getStartServices as any, + buildFlavor: 'serverless', + }) + ).toMatchInlineSnapshot(` + Object { + "id": "roles", + "mount": [Function], + "order": 20, + "title": "Custom Roles", + } + `); + }); + + it('mount() works for the `grid` page', async () => { + const { setBreadcrumbs, container, unmount, docTitle } = await mountApp('/', '/', 'serverless'); + + expect(setBreadcrumbs).toHaveBeenCalledTimes(1); + expect(setBreadcrumbs).toHaveBeenCalledWith([{ text: 'Custom Roles' }]); + expect(docTitle.change).toHaveBeenCalledWith('Custom Roles'); + expect(docTitle.reset).not.toHaveBeenCalled(); + expect(container).toMatchInlineSnapshot(` +
+ Roles Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"readOnly":false,"buildFlavor":"serverless","i18nStart":{},"theme":{"theme$":{}}} +
+ `); + + act(() => { + unmount(); + }); + + expect(docTitle.reset).toHaveBeenCalledTimes(1); + + expect(container).toMatchInlineSnapshot(`
`); + }); + + it('mount() works for the `create role` page', async () => { + const { setBreadcrumbs, container, unmount, docTitle } = await mountApp( + '/', + '/edit', + 'serverless' + ); + + expect(setBreadcrumbs).toHaveBeenCalledTimes(1); + expect(setBreadcrumbs).toHaveBeenCalledWith([ + { href: `/`, text: 'Custom Roles' }, + { text: 'Create' }, + ]); + expect(docTitle.change).toHaveBeenCalledWith('Custom Roles'); + expect(docTitle.reset).not.toHaveBeenCalled(); + expect(container).toMatchInlineSnapshot(` +
+ Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}},"buildFlavor":"serverless","i18nStart":{},"theme":{"theme$":{}}} +
+ `); + + act(() => { + unmount(); + }); + + expect(docTitle.reset).toHaveBeenCalledTimes(1); + + expect(container).toMatchInlineSnapshot(`
`); + }); + + it('mount() works for the `edit role` page', async () => { + const roleName = 'role@name'; + + const { setBreadcrumbs, container, unmount, docTitle } = await mountApp( + '/', + `/edit/${roleName}`, + 'serverless' + ); + + expect(setBreadcrumbs).toHaveBeenCalledTimes(1); + expect(setBreadcrumbs).toHaveBeenCalledWith([ + { href: `/`, text: 'Custom Roles' }, + { text: roleName }, + ]); + expect(docTitle.change).toHaveBeenCalledWith('Custom Roles'); + expect(docTitle.reset).not.toHaveBeenCalled(); + expect(container).toMatchInlineSnapshot(` +
+ Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}},"buildFlavor":"serverless","i18nStart":{},"theme":{"theme$":{}}} +
+ `); + + act(() => { + unmount(); + }); + + expect(docTitle.reset).toHaveBeenCalledTimes(1); + + expect(container).toMatchInlineSnapshot(`
`); + }); + + it('mount() works for the `clone role` page', async () => { + const roleName = 'someRoleName'; + + const { setBreadcrumbs, container, unmount, docTitle } = await mountApp( + '/', + `/clone/${roleName}`, + 'serverless' + ); + + expect(setBreadcrumbs).toHaveBeenCalledTimes(1); + expect(setBreadcrumbs).toHaveBeenCalledWith([ + { href: `/`, text: 'Custom Roles' }, + { text: 'Create' }, + ]); + expect(docTitle.change).toHaveBeenCalledWith('Custom Roles'); + expect(docTitle.reset).not.toHaveBeenCalled(); + expect(container).toMatchInlineSnapshot(` +
+ Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{},"roles":{"save":true}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}},"buildFlavor":"serverless","i18nStart":{},"theme":{"theme$":{}}} +
+ `); + + act(() => { + unmount(); + }); + + expect(docTitle.reset).toHaveBeenCalledTimes(1); + + expect(container).toMatchInlineSnapshot(`
`); + }); + + it('mount() properly encodes role name in `edit role` page link in breadcrumbs', async () => { + const roleName = 'some 安全性 role'; + + const { setBreadcrumbs } = await mountApp('/', `/edit/${roleName}`, 'serverless'); + + expect(setBreadcrumbs).toHaveBeenCalledTimes(1); + expect(setBreadcrumbs).toHaveBeenCalledWith([ + { href: `/`, text: 'Custom Roles' }, + { + text: roleName, + }, + ]); + }); +}); diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx index 704e975543724..f82548f89b352 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx @@ -50,7 +50,7 @@ export const rolesManagementApp = Object.freeze({ title, async mount({ element, theme$, setBreadcrumbs, history }) { const [ - [startServices, { dataViews, features, spaces }], + [startServices, { dataViews, features, spaces, cloud }], { RolesGridPage }, { EditRolePage }, { RolesAPIClient }, @@ -74,6 +74,7 @@ export const rolesManagementApp = Object.freeze({ i18n: i18nStart, notifications, chrome, + theme: themeServiceStart, } = startServices; chrome.docTitle.change(title); @@ -117,6 +118,10 @@ export const rolesManagementApp = Object.freeze({ dataViews={dataViews} history={history} spacesApiUi={spacesApiUi} + buildFlavor={buildFlavor} + i18nStart={i18nStart} + theme={themeServiceStart} + cloudOrgUrl={cloud?.organizationUrl} /> ); @@ -143,6 +148,10 @@ export const rolesManagementApp = Object.freeze({ rolesAPIClient={rolesAPIClient} history={history} readOnly={!startServices.application.capabilities.roles.save} + buildFlavor={buildFlavor} + i18nStart={i18nStart} + theme={themeServiceStart} + cloudOrgUrl={cloud?.organizationUrl} /> diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts b/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts index c6407e3784763..919144af7cadc 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts @@ -15,11 +15,14 @@ export function defineGetAllRolesRoutes({ authz, getFeatures, logger, + buildFlavor, + config, }: RouteDefinitionParams) { router.get( { path: '/api/security/role', validate: false }, createLicensedRouteHandler(async (context, request, response) => { try { + const hideReservedRoles = buildFlavor === 'serverless'; const esClient = (await context.core).elasticsearch.client; const [features, elasticsearchRoles] = await Promise.all([ getFeatures(), @@ -39,6 +42,9 @@ export function defineGetAllRolesRoutes({ logger ) ) + .filter((role) => { + return !hideReservedRoles || !role.metadata?._reserved; + }) .sort((roleA, roleB) => { if (roleA.name < roleB.name) { return -1; diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json index c73ecb5bd3146..2e0430de3b549 100644 --- a/x-pack/plugins/security/tsconfig.json +++ b/x-pack/plugins/security/tsconfig.json @@ -79,6 +79,9 @@ "@kbn/react-kibana-mount", "@kbn/react-kibana-context-theme", "@kbn/core-security-common", + "@kbn/react-kibana-context-common", + "@kbn/core-i18n-browser-mocks", + "@kbn/core-theme-browser-mocks", ], "exclude": [ "target/**/*", From 78711103f40f1f6cdb3c190329fed72034157d34 Mon Sep 17 00:00:00 2001 From: Alex Szabo Date: Wed, 17 Apr 2024 15:53:22 +0200 Subject: [PATCH 04/36] [BK] Adjust buildkite pipeline definition settings post migration (#181046) ## Summary Some defaults/generated-settings weren't aligned with what we had previously for these pipelines (from #180346 and #180403). This PR adjusts them manually to where they were. --- .../kibana-es-serverless-snapshots.yml | 4 ++-- .../kibana-es-snapshots.yml | 12 ++++++------ .../pipeline-resource-definitions/kibana-flaky.yml | 5 +++-- .../kibana-on-merge-unsupported-ftrs.yml | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml b/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml index 0cbf4bad865d0..b4d0978ecdbf4 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml @@ -22,7 +22,7 @@ spec: SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' ES_SERVERLESS_IMAGE: latest ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' - allow_rebuilds: false + allow_rebuilds: true branch_configuration: main default_branch: main repository: elastic/kibana @@ -35,7 +35,7 @@ spec: trigger_mode: none build_tags: false prefix_pull_request_fork_branch_names: false - skip_pull_request_builds_for_existing_commits: true + skip_pull_request_builds_for_existing_commits: false teams: everyone: access_level: BUILD_AND_READ diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml b/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml index 6691a460776ac..62eac086271b8 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml @@ -21,7 +21,7 @@ spec: env: SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' - allow_rebuilds: false + allow_rebuilds: true branch_configuration: main 8.13 7.17 default_branch: main repository: elastic/kibana @@ -34,7 +34,7 @@ spec: trigger_mode: none build_tags: false prefix_pull_request_fork_branch_names: false - skip_pull_request_builds_for_existing_commits: true + skip_pull_request_builds_for_existing_commits: false teams: everyone: access_level: BUILD_AND_READ @@ -81,7 +81,7 @@ spec: env: SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' - allow_rebuilds: false + allow_rebuilds: true branch_configuration: main 8.13 7.17 default_branch: main repository: elastic/kibana @@ -94,7 +94,7 @@ spec: trigger_mode: none build_tags: false prefix_pull_request_fork_branch_names: false - skip_pull_request_builds_for_existing_commits: true + skip_pull_request_builds_for_existing_commits: false teams: everyone: access_level: BUILD_AND_READ @@ -128,7 +128,7 @@ spec: env: SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' - allow_rebuilds: false + allow_rebuilds: true branch_configuration: main 8.13 7.17 default_branch: main repository: elastic/kibana @@ -141,7 +141,7 @@ spec: trigger_mode: none build_tags: false prefix_pull_request_fork_branch_names: false - skip_pull_request_builds_for_existing_commits: true + skip_pull_request_builds_for_existing_commits: false teams: everyone: access_level: BUILD_AND_READ diff --git a/.buildkite/pipeline-resource-definitions/kibana-flaky.yml b/.buildkite/pipeline-resource-definitions/kibana-flaky.yml index e31d4c635562f..82797c03f2378 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-flaky.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-flaky.yml @@ -18,17 +18,18 @@ spec: name: kibana / flaky-test-suite-runner description: ':warning: Trigger a new build here: https://ci-stats.kibana.dev/trigger_flaky_test_runner :warning:' spec: - allow_rebuilds: false + allow_rebuilds: true default_branch: refs/pull/INSERT_PR_NUMBER/head repository: elastic/kibana pipeline_file: .buildkite/pipelines/flaky_tests/pipeline.sh skip_intermediate_builds: false provider_settings: + build_branches: true build_pull_requests: false publish_commit_status: false trigger_mode: none prefix_pull_request_fork_branch_names: false - skip_pull_request_builds_for_existing_commits: true + skip_pull_request_builds_for_existing_commits: false teams: everyone: access_level: BUILD_AND_READ diff --git a/.buildkite/pipeline-resource-definitions/kibana-on-merge-unsupported-ftrs.yml b/.buildkite/pipeline-resource-definitions/kibana-on-merge-unsupported-ftrs.yml index 3188a44a5dbb3..4876e1e8f1bb4 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-on-merge-unsupported-ftrs.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-on-merge-unsupported-ftrs.yml @@ -21,7 +21,7 @@ spec: env: SLACK_NOTIFICATIONS_CHANNEL: '#kibana-unsupported-ftrs-alerts' ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' - allow_rebuilds: false + allow_rebuilds: true branch_configuration: main 8.13 7.17 default_branch: main repository: elastic/kibana @@ -34,7 +34,7 @@ spec: trigger_mode: none build_tags: false prefix_pull_request_fork_branch_names: false - skip_pull_request_builds_for_existing_commits: true + skip_pull_request_builds_for_existing_commits: false teams: everyone: access_level: BUILD_AND_READ From 873d48c3900d131626eb9738394bf325b927f3aa Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Wed, 17 Apr 2024 10:03:23 -0400 Subject: [PATCH 05/36] feat(slo): timeslice target value between 0 and 100% included (#180948) --- .../slo/docs/openapi/slo/bundled.json | 8 +++++++- .../slo/docs/openapi/slo/bundled.yaml | 6 ++++++ .../docs/openapi/slo/components/schemas/objective.yaml | 6 ++++++ .../slo_edit_form_objective_section_timeslices.tsx | 8 ++++---- .../slo/server/domain/services/validate_slo.test.ts | 4 ++-- .../slo/server/domain/services/validate_slo.ts | 10 +++++++--- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json index ca30e6b29ae49..11bf01c8a7328 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json @@ -1740,11 +1740,17 @@ "target": { "description": "the target objective between 0 and 1 excluded", "type": "number", + "minimum": 0, + "maximum": 100, + "exclusiveMinimum": true, + "exclusiveMaximum": true, "example": 0.99 }, "timesliceTarget": { "description": "the target objective for each slice when using a timeslices budgeting method", "type": "number", + "minimum": 0, + "maximum": 100, "example": 0.995 }, "timesliceWindow": { @@ -2472,4 +2478,4 @@ } } } -} +} \ No newline at end of file diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml index dc9e74407726f..d099474448eae 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml @@ -1187,10 +1187,16 @@ components: target: description: the target objective between 0 and 1 excluded type: number + minimum: 0 + maximum: 100 + exclusiveMinimum: true + exclusiveMaximum: true example: 0.99 timesliceTarget: description: the target objective for each slice when using a timeslices budgeting method type: number + minimum: 0 + maximum: 100 example: 0.995 timesliceWindow: description: the duration of each slice when using a timeslices budgeting method, as {duraton}{unit} diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/objective.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/objective.yaml index e0272c5a04029..a7cc6ae3d181b 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/objective.yaml +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/objective.yaml @@ -7,10 +7,16 @@ properties: target: description: the target objective between 0 and 1 excluded type: number + minimum: 0 + maximum: 100 + exclusiveMinimum: true + exclusiveMaximum: true example: 0.99 timesliceTarget: description: the target objective for each slice when using a timeslices budgeting method type: number + minimum: 0 + maximum: 100 example: 0.995 timesliceWindow: description: the duration of each slice when using a timeslices budgeting method, as {duraton}{unit} diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section_timeslices.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section_timeslices.tsx index 12e2bbed94b7b..b6d0b527eaf13 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section_timeslices.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section_timeslices.tsx @@ -41,8 +41,8 @@ export function SloEditFormObjectiveSectionTimeslices() { defaultValue={95} rules={{ required: true, - min: 0.001, - max: 99.999, + min: 0, + max: 100, }} render={({ field: { ref, onChange, ...field }, fieldState }) => ( onChange(event.target.value)} /> diff --git a/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts index f4f8142e8986a..7a4a6fc40005b 100644 --- a/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts @@ -132,12 +132,12 @@ describe('validateSLO', () => { expect(() => validateSLO(slo)).toThrowError('Invalid objective.timeslice_target'); }); - it("throws when 'objective.timeslice_target' is lte 0", () => { + it("throws when 'objective.timeslice_target' is lt 0", () => { const slo = createSLO({ budgetingMethod: 'timeslices', objective: { target: 0.95, - timesliceTarget: 0, + timesliceTarget: -0.00001, timesliceWindow: new Duration(1, DurationUnit.Minute), }, }); diff --git a/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.ts b/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.ts index d98e1a0ef40b6..fadf4ea08e6ea 100644 --- a/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.ts @@ -27,7 +27,7 @@ export function validateSLO(slo: SLODefinition) { throw new IllegalArgumentError('Invalid id'); } - if (!isValidTargetNumber(slo.objective.target)) { + if (!isValidObjectiveTarget(slo.objective.target)) { throw new IllegalArgumentError('Invalid objective.target'); } @@ -48,7 +48,7 @@ export function validateSLO(slo: SLODefinition) { if (timeslicesBudgetingMethodSchema.is(slo.budgetingMethod)) { if ( slo.objective.timesliceTarget === undefined || - !isValidTargetNumber(slo.objective.timesliceTarget) + !isValidTimesliceTarget(slo.objective.timesliceTarget) ) { throw new IllegalArgumentError('Invalid objective.timeslice_target'); } @@ -80,10 +80,14 @@ function isValidId(id: string): boolean { return MIN_ID_LENGTH <= id.length && id.length <= MAX_ID_LENGTH; } -function isValidTargetNumber(value: number): boolean { +function isValidObjectiveTarget(value: number): boolean { return value > 0 && value < 1; } +function isValidTimesliceTarget(value: number): boolean { + return value >= 0 && value <= 1; +} + function isValidRollingTimeWindowDuration(duration: Duration): boolean { // 7, 30 or 90days accepted return duration.unit === DurationUnit.Day && [7, 30, 90].includes(duration.value); From 59e936c580b296b1e060a34280aa3879a2fae60a Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 17 Apr 2024 17:13:05 +0300 Subject: [PATCH 06/36] fix: [Integrations > Install integration only][AXE-CORE]: Form elements must have accessible labels (#181037) Closes: https://github.com/elastic/security-team/issues/8978 ## Description The [axe browser plugin](https://deque.com/axe) is reporting multiple form elements missing accessible labels. I'll link the Kubernetes integration form because it had the most violations of the three integrations I tested (DynamoDB, Kubernetes, Windows). Screenshot below. ### Steps to recreate 1. Create a new Security Serverless project if none exist 2. When the project is ready, open it and go to Integrations, under the Project Settings in the lower left navigation 3. Search for Kubernetes in the Integrations, and click on the card 4. Click "Add Kubernetes" to load the prompt page 5. Click "Add integration only (skip agent installation)" 6. Open the Advanced settings menus, then run an axe check from your dev tools ### What was done?: 1. The `for` attribute was removed for `multi-row` mode (by setting `hasChildLabel={!varDef.multi}`). 2. A unique `aria-label` attribute was added for each `EuiFieldText` row. ### Screen: image --- .../steps/components/multi_text_input.tsx | 19 +++++++++++++++---- .../package_policy_input_var_field.tsx | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/multi_text_input.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/multi_text_input.tsx index ba17c3081f935..452e8bd55dcf5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/multi_text_input.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/multi_text_input.tsx @@ -38,7 +38,7 @@ interface RowProps { onBlur?: () => void; autoFocus?: boolean; isDisabled?: boolean; - showDeleteButton?: boolean; + isMultiRow?: boolean; } const Row: FunctionComponent = ({ @@ -50,7 +50,7 @@ const Row: FunctionComponent = ({ onBlur, autoFocus, isDisabled, - showDeleteButton, + isMultiRow, }) => { const onDeleteHandler = useCallback(() => { onDelete(index); @@ -73,10 +73,21 @@ const Row: FunctionComponent = ({ autoFocus={autoFocus} disabled={isDisabled} onBlur={onBlur} + aria-label={ + isMultiRow + ? i18n.translate('xpack.fleet.multiTextInput.ariaLabel', { + defaultMessage: '"{fieldLabel}" input {index}', + values: { + fieldLabel, + index: index + 1, + }, + }) + : fieldLabel + } data-test-subj={`multiTextInputRow-${index}`} /> - {showDeleteButton && ( + {isMultiRow && ( = ({ value={row} autoFocus={autoFocus} isDisabled={isDisabled} - showDeleteButton={rows.length > 1} + isMultiRow={rows.length > 1} /> ))} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx index 623523da56570..9464d60402a1d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx @@ -143,6 +143,7 @@ export const PackagePolicyInputVarField: React.FunctionComponent : fieldLabel} labelAppend={ isOptional ? ( From 87a8ad9e4aa1fc877537172705c6e567150baed7 Mon Sep 17 00:00:00 2001 From: Tomasz Ciecierski Date: Wed, 17 Apr 2024 16:25:41 +0200 Subject: [PATCH 07/36] [EDR Workflows] Missing validation in endpoint actions (#180790) --- .../endpoint/endpoint_actions_client.test.ts | 25 +++++++++++++++++++ .../endpoint/endpoint_actions_client.ts | 8 +++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts index 99445fb3e7dce..73948a4daf398 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts @@ -171,6 +171,31 @@ describe('EndpointActionsClient', () => { { meta: true } ); }); + it('should create an action with error when agents are invalid', async () => { + // @ts-expect-error mocking this for testing purposes + endpointActionsClient.checkAgentIds = jest.fn().mockResolvedValueOnce({ + isValid: false, + valid: [], + invalid: ['invalid-id'], + hosts: [{ agent: { id: 'invalid-id', name: '' }, host: { hostname: '' } }], + }); + + await endpointActionsClient.isolate(getCommonResponseActionOptions()); + + expect( + (await classConstructorOptions.endpointService.getFleetActionsClient()).create as jest.Mock + ).not.toHaveBeenCalled(); + expect(classConstructorOptions.esClient.index).toHaveBeenCalledWith( + expect.objectContaining({ + document: expect.objectContaining({ + error: { + message: 'The host does not have Elastic Defend integration installed', + }, + }), + }), + { meta: true } + ); + }); it('should return ActionDetails for newly created action', async () => { const actionResponse = await endpointActionsClient.isolate( diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts index c60d74b543e0c..08c1869256b2f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts @@ -82,8 +82,14 @@ export class EndpointActionsClient extends ResponseActionsClientImpl { ): Promise { const agentIds = await this.checkAgentIds(actionReq.endpoint_ids); const actionId = uuidv4(); + const { error: validationError } = await this.validateRequest({ + ...actionReq, + command, + endpoint_ids: agentIds.valid || [], + }); + const { hosts, ruleName, ruleId, error } = this.getMethodOptions(options); - let actionError: string | undefined = error; + let actionError: string | undefined = validationError?.message || error; // Dispatch action to Endpoint using Fleet if (!actionError) { From 4c90777deee632deccaf79dcbc37aa1189a5420a Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Wed, 17 Apr 2024 16:34:51 +0200 Subject: [PATCH 08/36] [Obs AI Assistant] Correct incorrect escaping in EVAL, WHERE (#180999) - `| WHERE statement LIKE 'SELECT%'` becomes `| WHERE statement LIKE "SELECT%"` - `| EVAL "@timestamp" = TO_DATETIME(timestamp)` becomes `| EVAL @timestamp = TO_DATETIME(timestamp)` --- .../correct_common_esql_mistakes.test.ts | 19 ++++++++- .../query/correct_common_esql_mistakes.ts | 42 ++++++++++++++++++- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.test.ts index ac56661af69fb..e9490a725a640 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.test.ts @@ -86,12 +86,27 @@ describe('correctCommonEsqlMistakes', () => { it(`escapes the column name if SORT uses an expression`, () => { expectQuery( 'FROM logs-* \n| STATS COUNT(*) by service.name\n| SORT COUNT(*) DESC', - 'FROM logs-*\n| STATS COUNT(*) by service.name\n| SORT `COUNT(*)` DESC' + 'FROM logs-*\n| STATS COUNT(*) BY service.name\n| SORT `COUNT(*)` DESC' ); expectQuery( 'FROM logs-* \n| STATS COUNT(*) by service.name\n| SORT COUNT(*) DESC, @timestamp ASC', - 'FROM logs-*\n| STATS COUNT(*) by service.name\n| SORT `COUNT(*)` DESC, @timestamp ASC' + 'FROM logs-*\n| STATS COUNT(*) BY service.name\n| SORT `COUNT(*)` DESC, @timestamp ASC' + ); + }); + + it(`handles complicated queries correctly`, () => { + expectQuery( + `FROM "postgres-logs*" + | GROK message "%{TIMESTAMP_ISO8601:timestamp} %{TZ} \[%{NUMBER:process_id}\]: \[%{NUMBER:log_line}\] user=%{USER:user},db=%{USER:database},app=\[%{DATA:application}\],client=%{IP:client_ip} LOG: duration: %{NUMBER:duration:float} ms statement: %{GREEDYDATA:statement}" + | EVAL "@timestamp" = TO_DATETIME(timestamp) + | WHERE statement LIKE 'SELECT%' + | STATS avg_duration = AVG(duration)`, + `FROM \`postgres-logs*\` + | GROK message "%{TIMESTAMP_ISO8601:timestamp} %{TZ} \[%{NUMBER:process_id}\]: \[%{NUMBER:log_line}\] user=%{USER:user},db=%{USER:database},app=\[%{DATA:application}\],client=%{IP:client_ip} LOG: duration: %{NUMBER:duration:float} ms statement: %{GREEDYDATA:statement}" + | EVAL @timestamp = TO_DATETIME(timestamp) + | WHERE statement LIKE "SELECT%" + | STATS avg_duration = AVG(duration)` ); }); }); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.ts index 8d65f06ba9f70..8b3f18359ce09 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.ts @@ -27,7 +27,10 @@ function split(value: string, splitToken: string) { if ( !delimiterToken && - trimmed.slice(index, index + splitToken.length).join('') === splitToken + trimmed + .slice(index, index + splitToken.length) + .join('') + .toLowerCase() === splitToken.toLowerCase() ) { index += splitToken.length - 1; statements.push(currentStatement.trim()); @@ -77,6 +80,16 @@ function replaceSingleQuotesWithDoubleQuotes(command: string) { return command.replace(regex, '"'); } +function removeColumnQuotesAndEscape(column: string) { + const plainColumnIdentifier = column.replaceAll(/^"(.*)"$/g, `$1`).replaceAll(/^'(.*)'$/g, `$1`); + + if (isValidColumnName(plainColumnIdentifier)) { + return plainColumnIdentifier; + } + + return '`' + plainColumnIdentifier + '`'; +} + function replaceAsKeywordWithAssignments(command: string) { return command.replaceAll(/^STATS\s*(.*)/g, (__, statsOperations: string) => { return `STATS ${statsOperations.replaceAll( @@ -87,7 +100,23 @@ function replaceAsKeywordWithAssignments(command: string) { } function isValidColumnName(column: string) { - return column.match(/^`.*`$/) || column.match(/^[@A-Za-z\._\-]+$/); + return Boolean(column.match(/^`.*`$/) || column.match(/^[@A-Za-z\._\-\d]+$/)); +} + +function escapeColumns(line: string) { + const [, command, body] = line.match(/^([A-Za-z_]+)(.*)$/) ?? ['', '', '']; + + const escapedBody = split(body.trim(), ',') + .map((statement) => { + const [lhs, rhs] = split(statement, '='); + if (!rhs) { + return lhs; + } + return `${removeColumnQuotesAndEscape(lhs)} = ${rhs}`; + }) + .join(', '); + + return `${command} ${escapedBody}`; } function verifyKeepColumns( @@ -182,12 +211,21 @@ export function correctCommonEsqlMistakes(content: string, log: Logger) { break; case 'WHERE': + formattedCommand = replaceSingleQuotesWithDoubleQuotes(formattedCommand); + break; + case 'EVAL': formattedCommand = replaceSingleQuotesWithDoubleQuotes(formattedCommand); + formattedCommand = escapeColumns(formattedCommand); break; case 'STATS': formattedCommand = replaceAsKeywordWithAssignments(formattedCommand); + const [before, after] = split(formattedCommand, ' BY '); + formattedCommand = escapeColumns(before); + if (after) { + formattedCommand += ` BY ${after}`; + } break; case 'KEEP': From e4a32f8f3c508b74c77d103637c8e3dfcd3d9d70 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Wed, 17 Apr 2024 07:52:41 -0700 Subject: [PATCH 09/36] [SharedUX] Remove usage of deprecated React rendering utilities (#180516) ## Summary Partially addresses https://github.com/elastic/kibana-team/issues/805 Follows https://github.com/elastic/kibana/pull/180003 These changes come up from searching in the code and finding where certain kinds of deprecated AppEx-SharedUX modules are imported. **Reviewers: Please interact with critical paths through the UI components touched in this PR, ESPECIALLY in terms of testing dark mode and i18n.** This focuses on code within AppEx-SharedUX. [Reporting changes are separate](https://github.com/elastic/kibana/pull/). image ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- examples/ui_action_examples/kibana.jsonc | 4 +-- .../public/hello_world_action.tsx | 15 ++++----- examples/ui_action_examples/public/plugin.ts | 8 ++--- examples/ui_action_examples/tsconfig.json | 2 +- examples/ui_actions_explorer/kibana.jsonc | 4 +-- .../public/actions/actions.tsx | 14 +++++--- examples/ui_actions_explorer/public/app.tsx | 11 +++---- .../public/hello_world_example.tsx | 13 ++++---- .../ui_actions_explorer/public/plugin.tsx | 7 ++-- examples/ui_actions_explorer/tsconfig.json | 2 +- src/plugins/files_management/tsconfig.json | 2 +- .../home/public/application/application.tsx | 25 +++++++------- src/plugins/home/public/plugin.ts | 2 +- src/plugins/home/tsconfig.json | 1 + .../kibana_overview/public/application.tsx | 24 +++++++------- src/plugins/kibana_overview/tsconfig.json | 1 + .../url_service/redirect/components/page.tsx | 4 +-- src/plugins/ui_actions/kibana.jsonc | 3 +- .../public/context_menu/open_context_menu.tsx | 8 ++--- src/plugins/ui_actions/public/plugin.ts | 10 +++--- .../public/service/ui_actions_service.test.ts | 8 +++-- src/plugins/ui_actions/public/services.ts | 4 ++- .../get_trigger_compatible_actions.test.ts | 7 ++-- .../tests/test_samples/hello_world_action.tsx | 12 +++++-- src/plugins/ui_actions/tsconfig.json | 3 +- .../kbn_sample_panel_action/kibana.jsonc | 4 +-- .../public/sample_panel_action.tsx | 9 +++-- .../kbn_sample_panel_action/tsconfig.json | 2 +- .../ui_actions_enhanced_examples/kibana.jsonc | 3 +- .../public/plugin.ts | 10 +++--- .../tsconfig.json | 2 +- x-pack/plugins/banners/kibana.jsonc | 4 +-- x-pack/plugins/banners/public/plugin.tsx | 8 ++--- x-pack/plugins/banners/tsconfig.json | 2 +- .../saved_objects_tagging/kibana.jsonc | 4 +-- .../assign_flyout/open_assign_flyout.tsx | 14 ++++---- .../components/edition_modal/open_modal.tsx | 28 ++++++++-------- .../public/management/actions/assign.ts | 20 ++++------- .../public/management/actions/edit.ts | 19 +++++------ .../public/management/actions/index.test.ts | 2 +- .../public/management/actions/index.ts | 14 ++++---- .../management/bulk_actions/bulk_assign.ts | 15 +++------ .../management/bulk_actions/index.test.ts | 2 +- .../public/management/bulk_actions/index.ts | 12 +++---- .../public/management/mount_section.tsx | 33 +++++++++---------- .../public/management/tag_management_page.tsx | 16 +++++---- .../saved_objects_tagging/public/plugin.ts | 7 ++-- .../saved_objects_tagging/public/types.ts | 6 ++++ .../public/ui_api/components.ts | 13 +++----- .../public/ui_api/index.ts | 15 +++------ .../saved_objects_tagging/tsconfig.json | 4 ++- x-pack/plugins/serverless/kibana.jsonc | 4 +-- x-pack/plugins/serverless/public/plugin.tsx | 15 ++++----- x-pack/plugins/serverless/tsconfig.json | 3 +- 54 files changed, 226 insertions(+), 258 deletions(-) diff --git a/examples/ui_action_examples/kibana.jsonc b/examples/ui_action_examples/kibana.jsonc index 3de8e301bae2e..2d7efc3de9cea 100644 --- a/examples/ui_action_examples/kibana.jsonc +++ b/examples/ui_action_examples/kibana.jsonc @@ -10,8 +10,6 @@ "requiredPlugins": [ "uiActions" ], - "requiredBundles": [ - "kibanaReact" - ] + "requiredBundles": [] } } diff --git a/examples/ui_action_examples/public/hello_world_action.tsx b/examples/ui_action_examples/public/hello_world_action.tsx index cb82f8f58a665..46d306f80b5e7 100644 --- a/examples/ui_action_examples/public/hello_world_action.tsx +++ b/examples/ui_action_examples/public/hello_world_action.tsx @@ -8,14 +8,12 @@ import React from 'react'; import { EuiText, EuiModalBody, EuiButton } from '@elastic/eui'; -import { OverlayStart } from '@kbn/core/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { CoreStart } from '@kbn/core/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; export const ACTION_HELLO_WORLD = 'ACTION_HELLO_WORLD'; -interface StartServices { - openModal: OverlayStart['openModal']; -} +type StartServices = Pick; export const createHelloWorldActionDefinition = ( getStartServices: () => Promise @@ -24,15 +22,16 @@ export const createHelloWorldActionDefinition = ( type: ACTION_HELLO_WORLD, getDisplayName: () => 'Hello World!', execute: async () => { - const { openModal } = await getStartServices(); - const overlay = openModal( + const { overlays, ...startServices } = await getStartServices(); + const overlay = overlays.openModal( toMountPoint( Hello world! overlay.close()}> Close - + , + startServices ) ); }, diff --git a/examples/ui_action_examples/public/plugin.ts b/examples/ui_action_examples/public/plugin.ts index 8b740b556cacc..286882ddde94d 100644 --- a/examples/ui_action_examples/public/plugin.ts +++ b/examples/ui_action_examples/public/plugin.ts @@ -29,14 +29,14 @@ export class UiActionExamplesPlugin ) { uiActions.registerTrigger(helloWorldTrigger); - const helloWorldAction = createHelloWorldActionDefinition(async () => ({ - openModal: (await core.getStartServices())[0].overlays.openModal, - })); + const helloWorldAction = createHelloWorldActionDefinition( + async () => (await core.getStartServices())[0] + ); uiActions.addTriggerAction(helloWorldTrigger.id, helloWorldAction); } - public start(core: CoreStart, plugins: UiActionExamplesStartDependencies) {} + public start(_core: CoreStart, _plugins: UiActionExamplesStartDependencies) {} public stop() {} } diff --git a/examples/ui_action_examples/tsconfig.json b/examples/ui_action_examples/tsconfig.json index b87f7cdf0d864..807e2eace4bc4 100644 --- a/examples/ui_action_examples/tsconfig.json +++ b/examples/ui_action_examples/tsconfig.json @@ -14,8 +14,8 @@ "target/**/*", ], "kbn_references": [ - "@kbn/kibana-react-plugin", "@kbn/ui-actions-plugin", "@kbn/core", + "@kbn/react-kibana-mount", ] } diff --git a/examples/ui_actions_explorer/kibana.jsonc b/examples/ui_actions_explorer/kibana.jsonc index e6c2c188c2f97..350a132a16cf7 100644 --- a/examples/ui_actions_explorer/kibana.jsonc +++ b/examples/ui_actions_explorer/kibana.jsonc @@ -12,8 +12,6 @@ "uiActionsExamples", "developerExamples" ], - "requiredBundles": [ - "kibanaReact" - ] + "requiredBundles": [] } } diff --git a/examples/ui_actions_explorer/public/actions/actions.tsx b/examples/ui_actions_explorer/public/actions/actions.tsx index 573778a504df3..63c50b1aec034 100644 --- a/examples/ui_actions_explorer/public/actions/actions.tsx +++ b/examples/ui_actions_explorer/public/actions/actions.tsx @@ -7,10 +7,10 @@ */ import React from 'react'; -import { OverlayStart } from '@kbn/core/public'; +import { CoreStart } from '@kbn/core/public'; import { EuiFieldText, EuiModalBody, EuiButton } from '@elastic/eui'; import { useState } from 'react'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { ActionExecutionContext, createAction, @@ -102,15 +102,19 @@ function EditUserModal({ ); } -export const createEditUserAction = (getOpenModal: () => Promise) => +export const createEditUserAction = (getStartServices: () => Promise) => createAction({ id: ACTION_EDIT_USER, type: ACTION_EDIT_USER, getIconType: () => 'pencil', getDisplayName: () => 'Edit user', execute: async ({ user, update }) => { - const overlay = (await getOpenModal())( - toMountPoint( overlay.close()} />) + const { overlays, ...startServices } = await getStartServices(); + const overlay = overlays.openModal( + toMountPoint( + overlay.close()} />, + startServices + ) ); }, }); diff --git a/examples/ui_actions_explorer/public/app.tsx b/examples/ui_actions_explorer/public/app.tsx index 31ac9c3c29ed9..3b5d3a99274f7 100644 --- a/examples/ui_actions_explorer/public/app.tsx +++ b/examples/ui_actions_explorer/public/app.tsx @@ -17,8 +17,8 @@ import { EuiSpacer, EuiPageHeader, } from '@elastic/eui'; +import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { AppMountParameters, OverlayStart } from '@kbn/core/public'; import { TriggerContextExample } from './trigger_context_example'; import { ContextMenuExamples } from './context_menu_examples'; import { Overview } from './overview'; @@ -26,10 +26,10 @@ import { HelloWorldExample } from './hello_world_example'; interface Props { uiActionsStartService: UiActionsStart; - openModal: OverlayStart['openModal']; + core: CoreStart; } -const ActionsExplorer = ({ uiActionsStartService, openModal }: Props) => { +const ActionsExplorer = ({ uiActionsStartService, core }: Props) => { return ( @@ -42,10 +42,7 @@ const ActionsExplorer = ({ uiActionsStartService, openModal }: Props) => { - + diff --git a/examples/ui_actions_explorer/public/hello_world_example.tsx b/examples/ui_actions_explorer/public/hello_world_example.tsx index 14fa8cae32606..89f5c23d00940 100644 --- a/examples/ui_actions_explorer/public/hello_world_example.tsx +++ b/examples/ui_actions_explorer/public/hello_world_example.tsx @@ -9,19 +9,19 @@ import React, { useState } from 'react'; import { EuiButton, EuiSpacer, EuiText, EuiModalBody, EuiLink, EuiSwitch } from '@elastic/eui'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { UiActionsStart, createAction } from '@kbn/ui-actions-plugin/public'; -import { OverlayStart } from '@kbn/core/public'; +import { CoreStart } from '@kbn/core/public'; import { HELLO_WORLD_TRIGGER_ID, ACTION_HELLO_WORLD } from '@kbn/ui-actions-examples-plugin/public'; const DYNAMIC_ACTION_ID = `${ACTION_HELLO_WORLD}-Waldo`; interface Props { uiActionsStartService: UiActionsStart; - openModal: OverlayStart['openModal']; + startServices: Pick; } -export const HelloWorldExample = ({ uiActionsStartService, openModal }: Props) => { +export const HelloWorldExample = ({ uiActionsStartService, startServices }: Props) => { const [isChecked, setIsChecked] = useState(false); const actionsMessage = isChecked ? '2 actions attached' : '1 action attached'; @@ -70,14 +70,15 @@ export const HelloWorldExample = ({ uiActionsStartService, openModal }: Props) = type: ACTION_HELLO_WORLD, getDisplayName: () => 'Say hello to Waldo', execute: async () => { - const overlay = openModal( + const overlay = startServices.overlays.openModal( toMountPoint( Hello Waldo{' '} overlay.close()}> Close - + , + startServices ) ); }, diff --git a/examples/ui_actions_explorer/public/plugin.tsx b/examples/ui_actions_explorer/public/plugin.tsx index fdb2357f40cdd..fc95dad91615a 100644 --- a/examples/ui_actions_explorer/public/plugin.tsx +++ b/examples/ui_actions_explorer/public/plugin.tsx @@ -51,7 +51,7 @@ export class UiActionsExplorerPlugin implements Plugin (await startServices)[0].overlays.openModal) + createEditUserAction(async () => (await startServices)[0]) ); deps.uiActions.addTriggerAction(COUNTRY_TRIGGER, viewInMapsAction); @@ -68,10 +68,7 @@ export class UiActionsExplorerPlugin implements Plugin, coreStart: CoreStart, history: ScopedHistory ) => { @@ -44,19 +43,19 @@ export const renderApp = async ( ); render( - - + + - - , + + , element ); }); @@ -64,7 +63,7 @@ export const renderApp = async ( // dispatch synthetic hash change event to update hash history objects // this is necessary because hash updates triggered by using popState won't trigger this event naturally. // This must be called before the app is mounted to avoid call this after the redirect to default app logic kicks in - const unlisten = history.listen((location) => { + const unlisten = history.listen((_location) => { window.dispatchEvent(new HashChangeEvent('hashchange')); }); diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index 8c895d9b491bd..84c383eb8a749 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -116,7 +116,7 @@ export class HomePublicPlugin i18n.translate('home.pageTitle', { defaultMessage: 'Home' }) ); const { renderApp } = await import('./application'); - return await renderApp(params.element, params.theme$, coreStart, params.history); + return await renderApp(params.element, coreStart, params.history); }, }); urlForwarding.forwardApp('home', 'home'); diff --git a/src/plugins/home/tsconfig.json b/src/plugins/home/tsconfig.json index 87137d6c7166e..01caa2a2c16cc 100644 --- a/src/plugins/home/tsconfig.json +++ b/src/plugins/home/tsconfig.json @@ -33,6 +33,7 @@ "@kbn/shared-ux-router", "@kbn/core-http-common", "@kbn/shared-ux-link-redirect-app", + "@kbn/react-kibana-context-render", ], "exclude": [ "target/**/*", diff --git a/src/plugins/kibana_overview/public/application.tsx b/src/plugins/kibana_overview/public/application.tsx index dce7573f63f53..8b94a09ba981b 100644 --- a/src/plugins/kibana_overview/public/application.tsx +++ b/src/plugins/kibana_overview/public/application.tsx @@ -9,8 +9,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; -import { I18nProvider } from '@kbn/i18n-react'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { NewsfeedApiEndpoint } from '@kbn/newsfeed-plugin/public'; import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { AppPluginStartDependencies } from './types'; @@ -19,7 +19,7 @@ import { KibanaOverviewApp } from './components/app'; export const renderApp = ( core: CoreStart, deps: AppPluginStartDependencies, - { appBasePath, element, theme$ }: AppMountParameters + { appBasePath, element }: AppMountParameters ) => { const { notifications, http } = core; const { newsfeed, home, navigation } = deps; @@ -41,16 +41,14 @@ export const renderApp = ( ); ReactDOM.render( - - - - - - - , + + + + + , element ); }); diff --git a/src/plugins/kibana_overview/tsconfig.json b/src/plugins/kibana_overview/tsconfig.json index 09e9d232901c4..ad6a946b432b5 100644 --- a/src/plugins/kibana_overview/tsconfig.json +++ b/src/plugins/kibana_overview/tsconfig.json @@ -27,6 +27,7 @@ "@kbn/shared-ux-router", "@kbn/shared-ux-avatar-solution", "@kbn/shared-ux-utility", + "@kbn/react-kibana-context-render", ], "exclude": [ "target/**/*", diff --git a/src/plugins/share/public/url_service/redirect/components/page.tsx b/src/plugins/share/public/url_service/redirect/components/page.tsx index e06461a91b64c..1e6293691fb45 100644 --- a/src/plugins/share/public/url_service/redirect/components/page.tsx +++ b/src/plugins/share/public/url_service/redirect/components/page.tsx @@ -38,7 +38,7 @@ export const Page: React.FC = ({ if (error) { return ( - + @@ -47,7 +47,7 @@ export const Page: React.FC = ({ } return ( - + diff --git a/src/plugins/ui_actions/kibana.jsonc b/src/plugins/ui_actions/kibana.jsonc index 66ccaa6917d0b..e63c80190c074 100644 --- a/src/plugins/ui_actions/kibana.jsonc +++ b/src/plugins/ui_actions/kibana.jsonc @@ -9,8 +9,7 @@ "browser": true, "requiredPlugins": [], "requiredBundles": [ - "kibanaUtils", - "kibanaReact" + "kibanaUtils" ] } } diff --git a/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx b/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx index 8c7cf620d4a82..bac4e57aa5624 100644 --- a/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx +++ b/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx @@ -11,8 +11,8 @@ import React from 'react'; import { EuiContextMenu, EuiContextMenuPanelDescriptor, EuiPopover } from '@elastic/eui'; import { EventEmitter } from 'events'; import ReactDOM from 'react-dom'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { getTheme } from '../services'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { getAnalytics, getI18n, getTheme } from '../services'; let activeSession: ContextMenuSession | null = null; @@ -170,7 +170,7 @@ export function openContextMenu( }; ReactDOM.render( - + - , + , container ); diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 1a0e45612c831..1dbff5b9729a0 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -14,7 +14,7 @@ import { visualizeGeoFieldTrigger, } from '@kbn/ui-actions-browser/src/triggers'; import { UiActionsService } from './service'; -import { setTheme } from './services'; +import { setAnalytics, setI18n, setTheme } from './services'; export type UiActionsPublicSetup = Pick< UiActionsService, @@ -47,15 +47,17 @@ export class UiActionsPlugin constructor(_initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup): UiActionsPublicSetup { - setTheme(core.theme); + public setup(_core: CoreSetup): UiActionsPublicSetup { this.service.registerTrigger(rowClickTrigger); this.service.registerTrigger(visualizeFieldTrigger); this.service.registerTrigger(visualizeGeoFieldTrigger); return this.service; } - public start(_core: CoreStart): UiActionsPublicStart { + public start(core: CoreStart): UiActionsPublicStart { + setAnalytics(core.analytics); + setI18n(core.i18n); + setTheme(core.theme); return this.service; } diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.test.ts b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts index 42e3c4ea593fa..b317b343b3ade 100644 --- a/src/plugins/ui_actions/public/service/ui_actions_service.test.ts +++ b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts @@ -10,8 +10,8 @@ import { UiActionsService } from './ui_actions_service'; import { ActionDefinition, ActionInternal } from '../actions'; import { createHelloWorldAction } from '../tests/test_samples'; import { TriggerRegistry, ActionRegistry } from '../types'; +import { coreMock } from '@kbn/core/public/mocks'; import type { Trigger } from '@kbn/ui-actions-browser/src/triggers'; -import { OverlayStart } from '@kbn/core/public'; const FOO_TRIGGER = 'FOO_TRIGGER'; const BAR_TRIGGER = 'BAR_TRIGGER'; @@ -159,10 +159,12 @@ describe('UiActionsService', () => { }); describe('.getTriggerCompatibleActions()', () => { + const coreStart = coreMock.createStart(); + test('can register and get actions', async () => { const actions: ActionRegistry = new Map(); const service = new UiActionsService({ actions }); - const helloWorldAction = createHelloWorldAction({} as unknown as OverlayStart); + const helloWorldAction = createHelloWorldAction(coreStart); const length = actions.size; service.registerAction(helloWorldAction); @@ -173,7 +175,7 @@ describe('UiActionsService', () => { test('getTriggerCompatibleActions returns attached actions', async () => { const service = new UiActionsService(); - const helloWorldAction = createHelloWorldAction({} as unknown as OverlayStart); + const helloWorldAction = createHelloWorldAction(coreStart); service.registerAction(helloWorldAction); diff --git a/src/plugins/ui_actions/public/services.ts b/src/plugins/ui_actions/public/services.ts index 60c4b81311960..427d0f2ec20d2 100644 --- a/src/plugins/ui_actions/public/services.ts +++ b/src/plugins/ui_actions/public/services.ts @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -import { ThemeServiceSetup } from '@kbn/core/public'; +import { AnalyticsServiceStart, I18nStart, ThemeServiceSetup } from '@kbn/core/public'; import { createGetterSetter } from '@kbn/kibana-utils-plugin/public'; +export const [getAnalytics, setAnalytics] = createGetterSetter('Analytics'); +export const [getI18n, setI18n] = createGetterSetter('I18n'); export const [getTheme, setTheme] = createGetterSetter('Theme'); diff --git a/src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts b/src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts index 6599d66d2081e..4fa2ef752aa91 100644 --- a/src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts +++ b/src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts @@ -9,9 +9,10 @@ import { uiActionsPluginMock } from '../mocks'; import { createHelloWorldAction } from './test_samples'; import { ActionDefinition } from '../actions'; +import { coreMock } from '@kbn/core/public/mocks'; import type { Trigger } from '@kbn/ui-actions-browser'; -import { OverlayStart } from '@kbn/core/public'; +const coreStart = coreMock.createStart(); let action: ActionDefinition<{ name: string }>; let uiActions: ReturnType; beforeEach(() => { @@ -32,14 +33,14 @@ beforeEach(() => { test('can register action', async () => { const { setup } = uiActions; - const helloWorldAction = createHelloWorldAction({} as unknown as OverlayStart); + const helloWorldAction = createHelloWorldAction(coreStart); setup.registerAction(helloWorldAction); }); test('getTriggerCompatibleActions returns attached actions', async () => { const { setup, doStart } = uiActions; - const helloWorldAction = createHelloWorldAction({} as unknown as OverlayStart); + const helloWorldAction = createHelloWorldAction(coreStart); setup.registerAction(helloWorldAction); diff --git a/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx b/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx index 66f8544b093ed..8df43b51a020c 100644 --- a/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx +++ b/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiBadge, EuiFlyoutBody } from '@elastic/eui'; import { CoreStart } from '@kbn/core/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { ActionDefinition } from '../../actions'; const MenuItem: React.FC = () => { @@ -25,7 +25,10 @@ const MenuItem: React.FC = () => { export const ACTION_HELLO_WORLD = 'ACTION_HELLO_WORLD'; -export function createHelloWorldAction(overlays: CoreStart['overlays']): ActionDefinition { +export function createHelloWorldAction( + coreStart: Pick +): ActionDefinition { + const { overlays, ...startServices } = coreStart; return { id: ACTION_HELLO_WORLD, type: ACTION_HELLO_WORLD, @@ -33,7 +36,10 @@ export function createHelloWorldAction(overlays: CoreStart['overlays']): ActionD MenuItem, execute: async () => { overlays.openFlyout( - toMountPoint(Hello World, I am a hello world action!), + toMountPoint( + Hello World, I am a hello world action!, + startServices + ), { 'data-test-subj': 'helloWorldAction', ownFocus: true, diff --git a/src/plugins/ui_actions/tsconfig.json b/src/plugins/ui_actions/tsconfig.json index bac1778817bb2..4601d4b2732e1 100644 --- a/src/plugins/ui_actions/tsconfig.json +++ b/src/plugins/ui_actions/tsconfig.json @@ -7,13 +7,14 @@ "kbn_references": [ "@kbn/core", "@kbn/kibana-utils-plugin", - "@kbn/kibana-react-plugin", "@kbn/data-views-plugin", "@kbn/utility-types", "@kbn/i18n", "@kbn/es-query", "@kbn/ui-actions-browser", "@kbn/expressions-plugin", + "@kbn/react-kibana-context-render", + "@kbn/react-kibana-mount", ], "exclude": [ "target/**/*", diff --git a/test/plugin_functional/plugins/kbn_sample_panel_action/kibana.jsonc b/test/plugin_functional/plugins/kbn_sample_panel_action/kibana.jsonc index 8c52ce4f7e2b6..94944389f81e7 100644 --- a/test/plugin_functional/plugins/kbn_sample_panel_action/kibana.jsonc +++ b/test/plugin_functional/plugins/kbn_sample_panel_action/kibana.jsonc @@ -13,8 +13,6 @@ "uiActions", "embeddable" ], - "requiredBundles": [ - "kibanaReact" - ] + "requiredBundles": [] } } diff --git a/test/plugin_functional/plugins/kbn_sample_panel_action/public/sample_panel_action.tsx b/test/plugin_functional/plugins/kbn_sample_panel_action/public/sample_panel_action.tsx index e35e10b4808b0..aa92d75fc0c13 100644 --- a/test/plugin_functional/plugins/kbn_sample_panel_action/public/sample_panel_action.tsx +++ b/test/plugin_functional/plugins/kbn_sample_panel_action/public/sample_panel_action.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { IEmbeddable } from '@kbn/embeddable-plugin/public'; import { createAction } from '@kbn/ui-actions-plugin/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; export const SAMPLE_PANEL_ACTION = 'samplePanelAction'; @@ -29,7 +29,9 @@ export function createSamplePanelAction(getStartServices: CoreSetup['getStartSer if (!embeddable) { return; } - const openFlyout = (await getStartServices())[0].overlays.openFlyout; + const coreStart = (await getStartServices())[0]; + const { overlays, ...startServices } = coreStart; + const openFlyout = overlays.openFlyout; openFlyout( toMountPoint( @@ -41,7 +43,8 @@ export function createSamplePanelAction(getStartServices: CoreSetup['getStartSer

This is a sample action

-
+ , + startServices ), { 'data-test-subj': 'samplePanelActionFlyout', diff --git a/test/plugin_functional/plugins/kbn_sample_panel_action/tsconfig.json b/test/plugin_functional/plugins/kbn_sample_panel_action/tsconfig.json index a3fe79437f30d..506a7dba6fd16 100644 --- a/test/plugin_functional/plugins/kbn_sample_panel_action/tsconfig.json +++ b/test/plugin_functional/plugins/kbn_sample_panel_action/tsconfig.json @@ -16,6 +16,6 @@ "@kbn/core", "@kbn/ui-actions-plugin", "@kbn/embeddable-plugin", - "@kbn/kibana-react-plugin", + "@kbn/react-kibana-mount", ] } diff --git a/x-pack/examples/ui_actions_enhanced_examples/kibana.jsonc b/x-pack/examples/ui_actions_enhanced_examples/kibana.jsonc index 33412d3529b47..1da3e4f182875 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/kibana.jsonc +++ b/x-pack/examples/ui_actions_enhanced_examples/kibana.jsonc @@ -22,8 +22,7 @@ "requiredBundles": [ "dashboardEnhanced", "embeddable", - "kibanaUtils", - "kibanaReact" + "kibanaUtils" ] } } diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts b/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts index 2cac8f3bac939..fbd74fc581012 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts +++ b/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts @@ -6,7 +6,7 @@ */ import { createElement as h } from 'react'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { Plugin, CoreSetup, CoreStart } from '@kbn/core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; import { @@ -93,7 +93,8 @@ export class UiActionsEnhancedExamplesPlugin dynamicActionManager: self.managerWithoutEmbeddableSingleButton, triggers: [SAMPLE_APP2_CLICK_TRIGGER], placeContext: {}, - }) + }), + coreStart ), { ownFocus: true, @@ -118,7 +119,8 @@ export class UiActionsEnhancedExamplesPlugin dynamicActionManager: self.managerWithoutEmbeddableSingleButton, triggers: [SAMPLE_APP2_CLICK_TRIGGER], placeContext: { sampleApp2ClickContext }, - }) + }), + coreStart ), { ownFocus: true, @@ -150,7 +152,7 @@ export class UiActionsEnhancedExamplesPlugin }); } - public start(core: CoreStart, plugins: StartDependencies): UiActionsEnhancedExamplesStart { + public start(_core: CoreStart, plugins: StartDependencies): UiActionsEnhancedExamplesStart { const managerWithoutEmbeddable = new UiActionsEnhancedDynamicActionManager({ storage: new UiActionsEnhancedMemoryActionStorage(), isCompatible: async () => true, diff --git a/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json b/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json index ef56838b5deba..6010bece3bd1c 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json +++ b/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json @@ -16,7 +16,6 @@ "kbn_references": [ "@kbn/core", "@kbn/kibana-utils-plugin", - "@kbn/kibana-react-plugin", "@kbn/share-plugin", "@kbn/discover-plugin", "@kbn/dashboard-plugin", @@ -30,5 +29,6 @@ "@kbn/unified-search-plugin", "@kbn/utility-types", "@kbn/presentation-publishing", + "@kbn/react-kibana-mount", ] } diff --git a/x-pack/plugins/banners/kibana.jsonc b/x-pack/plugins/banners/kibana.jsonc index ffd3ced829961..75d275a6bde4a 100644 --- a/x-pack/plugins/banners/kibana.jsonc +++ b/x-pack/plugins/banners/kibana.jsonc @@ -17,8 +17,6 @@ "optionalPlugins": [ "screenshotMode" ], - "requiredBundles": [ - "kibanaReact" - ] + "requiredBundles": [] } } diff --git a/x-pack/plugins/banners/public/plugin.tsx b/x-pack/plugins/banners/public/plugin.tsx index 8d6d90e088a99..94c7d6c074379 100644 --- a/x-pack/plugins/banners/public/plugin.tsx +++ b/x-pack/plugins/banners/public/plugin.tsx @@ -7,20 +7,20 @@ import React from 'react'; import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { Banner } from './components'; import { getBannerInfo } from './get_banner_info'; import { BannerPluginStartDependencies } from './types'; export class BannersPlugin implements Plugin<{}, {}, {}, BannerPluginStartDependencies> { - constructor(context: PluginInitializerContext) {} + constructor(_context: PluginInitializerContext) {} setup({}: CoreSetup<{}, {}>) { return {}; } start( - { chrome, uiSettings, http }: CoreStart, + { chrome, http, ...startServices }: CoreStart, { screenshotMode }: BannerPluginStartDependencies ) { if (!(screenshotMode?.isScreenshotMode() ?? false)) { @@ -28,7 +28,7 @@ export class BannersPlugin implements Plugin<{}, {}, {}, BannerPluginStartDepend ({ allowed, banner }) => { if (allowed && banner.placement === 'top') { chrome.setHeaderBanner({ - content: toMountPoint(), + content: toMountPoint(, startServices), }); } }, diff --git a/x-pack/plugins/banners/tsconfig.json b/x-pack/plugins/banners/tsconfig.json index 019a4ebd7da88..978c977d0fe2c 100644 --- a/x-pack/plugins/banners/tsconfig.json +++ b/x-pack/plugins/banners/tsconfig.json @@ -6,12 +6,12 @@ "include": ["public/**/*", "server/**/*", "common/**/*", "../../../typings/**/*"], "kbn_references": [ "@kbn/core", - "@kbn/kibana-react-plugin", "@kbn/screenshot-mode-plugin", "@kbn/licensing-plugin", "@kbn/config-schema", "@kbn/i18n", "@kbn/shared-ux-markdown", + "@kbn/react-kibana-mount", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/saved_objects_tagging/kibana.jsonc b/x-pack/plugins/saved_objects_tagging/kibana.jsonc index 7daa89f63ba37..a3c5609148d99 100644 --- a/x-pack/plugins/saved_objects_tagging/kibana.jsonc +++ b/x-pack/plugins/saved_objects_tagging/kibana.jsonc @@ -19,8 +19,6 @@ "usageCollection", "security" ], - "requiredBundles": [ - "kibanaReact" - ] + "requiredBundles": [] } } diff --git a/x-pack/plugins/saved_objects_tagging/public/components/assign_flyout/open_assign_flyout.tsx b/x-pack/plugins/saved_objects_tagging/public/components/assign_flyout/open_assign_flyout.tsx index 175c20322607c..92c5159378cf3 100644 --- a/x-pack/plugins/saved_objects_tagging/public/components/assign_flyout/open_assign_flyout.tsx +++ b/x-pack/plugins/saved_objects_tagging/public/components/assign_flyout/open_assign_flyout.tsx @@ -7,14 +7,12 @@ import React from 'react'; import { EuiDelayRender, EuiLoadingSpinner } from '@elastic/eui'; -import { NotificationsStart, OverlayStart, ThemeServiceStart, OverlayRef } from '@kbn/core/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { OverlayRef } from '@kbn/core/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { ITagAssignmentService, ITagsCache } from '../../services'; +import { StartServices } from '../../types'; -export interface GetAssignFlyoutOpenerOptions { - overlays: OverlayStart; - notifications: NotificationsStart; - theme: ThemeServiceStart; +export interface GetAssignFlyoutOpenerOptions extends StartServices { tagCache: ITagsCache; assignmentService: ITagAssignmentService; assignableTypes: string[]; @@ -43,10 +41,10 @@ export const getAssignFlyoutOpener = ({ overlays, notifications, - theme, tagCache, assignmentService, assignableTypes, + ...startServices }: GetAssignFlyoutOpenerOptions): AssignFlyoutOpener => async ({ tagIds }) => { const flyout = overlays.openFlyout( @@ -61,7 +59,7 @@ export const getAssignFlyoutOpener = onClose={() => flyout.close()} /> , - { theme$: theme.theme$ } + startServices ), { size: 'm', maxWidth: 600 } ); diff --git a/x-pack/plugins/saved_objects_tagging/public/components/edition_modal/open_modal.tsx b/x-pack/plugins/saved_objects_tagging/public/components/edition_modal/open_modal.tsx index 9fb8dc28c466e..63a784aa09c4d 100644 --- a/x-pack/plugins/saved_objects_tagging/public/components/edition_modal/open_modal.tsx +++ b/x-pack/plugins/saved_objects_tagging/public/components/edition_modal/open_modal.tsx @@ -7,20 +7,13 @@ import React from 'react'; import { EuiDelayRender, EuiLoadingSpinner } from '@elastic/eui'; -import type { - OverlayStart, - OverlayRef, - ThemeServiceStart, - NotificationsStart, -} from '@kbn/core/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import type { OverlayRef } from '@kbn/core/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { Tag, TagAttributes } from '../../../common/types'; import { ITagInternalClient } from '../../services'; +import { StartServices } from '../../types'; -interface GetModalOpenerOptions { - overlays: OverlayStart; - notifications: NotificationsStart; - theme: ThemeServiceStart; +interface GetModalOpenerOptions extends StartServices { tagClient: ITagInternalClient; } @@ -46,7 +39,12 @@ const LazyEditTagModal = React.lazy(() => ); export const getCreateModalOpener = - ({ overlays, theme, tagClient, notifications }: GetModalOpenerOptions): CreateModalOpener => + ({ + overlays, + tagClient, + notifications, + ...startServices + }: GetModalOpenerOptions): CreateModalOpener => async ({ onCreate, defaultValues }: OpenCreateModalOptions) => { const modal = overlays.openModal( toMountPoint( @@ -64,7 +62,7 @@ export const getCreateModalOpener = notifications={notifications} /> , - { theme$: theme.theme$ } + startServices ) ); return modal; @@ -76,7 +74,7 @@ interface OpenEditModalOptions { } export const getEditModalOpener = - ({ overlays, theme, tagClient, notifications }: GetModalOpenerOptions) => + ({ overlays, tagClient, notifications, ...startServices }: GetModalOpenerOptions) => async ({ tagId, onUpdate }: OpenEditModalOptions) => { const tag = await tagClient.get(tagId); @@ -96,7 +94,7 @@ export const getEditModalOpener = notifications={notifications} /> , - { theme$: theme.theme$ } + startServices ) ); diff --git a/x-pack/plugins/saved_objects_tagging/public/management/actions/assign.ts b/x-pack/plugins/saved_objects_tagging/public/management/actions/assign.ts index 22ab4dc95a2fc..7513b9d89c14b 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/actions/assign.ts +++ b/x-pack/plugins/saved_objects_tagging/public/management/actions/assign.ts @@ -5,20 +5,16 @@ * 2.0. */ -import { Observable, from } from 'rxjs'; -import { takeUntil } from 'rxjs'; import { i18n } from '@kbn/i18n'; -import { NotificationsStart, OverlayStart, ThemeServiceStart } from '@kbn/core/public'; +import { Observable, from, takeUntil } from 'rxjs'; import { TagWithRelations } from '../../../common'; -import { ITagsCache } from '../../services/tags'; import { getAssignFlyoutOpener } from '../../components/assign_flyout'; import { ITagAssignmentService } from '../../services/assignments'; +import { ITagsCache } from '../../services/tags'; +import { StartServices } from '../../types'; import { TagAction } from './types'; -interface GetAssignActionOptions { - overlays: OverlayStart; - notifications: NotificationsStart; - theme: ThemeServiceStart; +interface GetAssignActionOptions extends StartServices { tagCache: ITagsCache; assignmentService: ITagAssignmentService; assignableTypes: string[]; @@ -27,19 +23,15 @@ interface GetAssignActionOptions { } export const getAssignAction = ({ - notifications, - overlays, - theme, assignableTypes, assignmentService, tagCache, fetchTags, canceled$, + ...startServices }: GetAssignActionOptions): TagAction => { const openFlyout = getAssignFlyoutOpener({ - overlays, - notifications, - theme, + ...startServices, tagCache, assignmentService, assignableTypes, diff --git a/x-pack/plugins/saved_objects_tagging/public/management/actions/edit.ts b/x-pack/plugins/saved_objects_tagging/public/management/actions/edit.ts index 8d419e5d8aab3..87b1d4ff32fd3 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/actions/edit.ts +++ b/x-pack/plugins/saved_objects_tagging/public/management/actions/edit.ts @@ -6,28 +6,26 @@ */ import { i18n } from '@kbn/i18n'; -import { NotificationsStart, OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import { TagWithRelations } from '../../../common'; -import { ITagInternalClient } from '../../services/tags'; import { getEditModalOpener } from '../../components/edition_modal'; +import { ITagInternalClient } from '../../services/tags'; +import { StartServices } from '../../types'; import { TagAction } from './types'; -interface GetEditActionOptions { - overlays: OverlayStart; - theme: ThemeServiceStart; - notifications: NotificationsStart; +interface GetEditActionOptions extends StartServices { tagClient: ITagInternalClient; fetchTags: () => Promise; } export const getEditAction = ({ - notifications, - theme, - overlays, tagClient, fetchTags, + ...startServices }: GetEditActionOptions): TagAction => { - const editModalOpener = getEditModalOpener({ overlays, theme, tagClient, notifications }); + const editModalOpener = getEditModalOpener({ + ...startServices, + tagClient, + }); return { id: 'edit', name: ({ name }) => @@ -46,6 +44,7 @@ export const getEditAction = ({ icon: 'pencil', available: (tag) => !tag.managed, onClick: (tag: TagWithRelations) => { + const { notifications } = startServices; editModalOpener({ tagId: tag.id, onUpdate: (updatedTag) => { diff --git a/x-pack/plugins/saved_objects_tagging/public/management/actions/index.test.ts b/x-pack/plugins/saved_objects_tagging/public/management/actions/index.test.ts index 8aa7b160b0c21..bffc8c35bcc40 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/actions/index.test.ts +++ b/x-pack/plugins/saved_objects_tagging/public/management/actions/index.test.ts @@ -36,7 +36,7 @@ describe('getTableActions', () => { { assignableTypes = ['foo', 'bar'] }: { assignableTypes?: string[] } = {} ) => getTableActions({ - core, + startServices: core, tagClient, tagCache, assignmentService, diff --git a/x-pack/plugins/saved_objects_tagging/public/management/actions/index.ts b/x-pack/plugins/saved_objects_tagging/public/management/actions/index.ts index 2d143faa8fa78..6feffde001f86 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/actions/index.ts +++ b/x-pack/plugins/saved_objects_tagging/public/management/actions/index.ts @@ -6,9 +6,9 @@ */ import { Observable } from 'rxjs'; -import { CoreStart } from '@kbn/core/public'; import { TagsCapabilities } from '../../../common'; import { ITagInternalClient, ITagAssignmentService, ITagsCache } from '../../services'; +import { StartServices } from '../../types'; import { TagAction } from './types'; import { getDeleteAction } from './delete'; import { getEditAction } from './edit'; @@ -17,7 +17,7 @@ import { getAssignAction } from './assign'; export type { TagAction } from './types'; interface GetActionsOptions { - core: CoreStart; + startServices: StartServices; capabilities: TagsCapabilities; tagClient: ITagInternalClient; tagCache: ITagsCache; @@ -29,7 +29,7 @@ interface GetActionsOptions { } export const getTableActions = ({ - core: { notifications, overlays, theme }, + startServices, capabilities, tagClient, tagCache, @@ -41,26 +41,24 @@ export const getTableActions = ({ const actions: TagAction[] = []; if (capabilities.edit) { - actions.push(getEditAction({ notifications, overlays, theme, tagClient, fetchTags })); + actions.push(getEditAction({ ...startServices, tagClient, fetchTags })); } if (capabilities.assign && assignableTypes.length > 0) { actions.push( getAssignAction({ + ...startServices, tagCache, assignmentService, assignableTypes, fetchTags, - notifications, - overlays, - theme, canceled$, }) ); } if (capabilities.delete) { - actions.push(getDeleteAction({ overlays, notifications, tagClient, fetchTags })); + actions.push(getDeleteAction({ ...startServices, tagClient, fetchTags })); } return actions; diff --git a/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/bulk_assign.ts b/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/bulk_assign.ts index bbb2081b198cf..cd02adad32b95 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/bulk_assign.ts +++ b/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/bulk_assign.ts @@ -7,16 +7,13 @@ import { from } from 'rxjs'; import { takeUntil } from 'rxjs'; -import { OverlayStart, NotificationsStart, ThemeServiceStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { ITagsCache, ITagAssignmentService } from '../../services'; +import { StartServices } from '../../types'; import { TagBulkAction } from '../types'; import { getAssignFlyoutOpener } from '../../components/assign_flyout'; -interface GetBulkAssignActionOptions { - overlays: OverlayStart; - notifications: NotificationsStart; - theme: ThemeServiceStart; +interface GetBulkAssignActionOptions extends StartServices { tagCache: ITagsCache; assignmentService: ITagAssignmentService; assignableTypes: string[]; @@ -24,17 +21,13 @@ interface GetBulkAssignActionOptions { } export const getBulkAssignAction = ({ - overlays, - notifications, - theme, tagCache, assignmentService, assignableTypes, + ...startServices }: GetBulkAssignActionOptions): TagBulkAction => { const openFlyout = getAssignFlyoutOpener({ - overlays, - notifications, - theme, + ...startServices, tagCache, assignmentService, assignableTypes, diff --git a/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/index.test.ts b/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/index.test.ts index 43935c9ab441f..dd3e446dad1e3 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/index.test.ts +++ b/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/index.test.ts @@ -37,7 +37,7 @@ describe('getBulkActions', () => { { assignableTypes = ['foo', 'bar'] }: { assignableTypes?: string[] } = {} ) => getBulkActions({ - core, + startServices: core, tagClient, tagCache, assignmentService, diff --git a/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/index.ts b/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/index.ts index c7fba98247919..ea0b8cfb42eb5 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/index.ts +++ b/x-pack/plugins/saved_objects_tagging/public/management/bulk_actions/index.ts @@ -5,16 +5,16 @@ * 2.0. */ -import { CoreStart } from '@kbn/core/public'; import { TagsCapabilities } from '../../../common'; import { ITagInternalClient, ITagAssignmentService, ITagsCache } from '../../services'; +import { StartServices } from '../../types'; import { TagBulkAction } from '../types'; import { getBulkDeleteAction } from './bulk_delete'; import { getBulkAssignAction } from './bulk_assign'; import { getClearSelectionAction } from './clear_selection'; interface GetBulkActionOptions { - core: CoreStart; + startServices: StartServices; capabilities: TagsCapabilities; tagClient: ITagInternalClient; tagCache: ITagsCache; @@ -25,7 +25,7 @@ interface GetBulkActionOptions { } export const getBulkActions = ({ - core: { notifications, overlays, theme }, + startServices, capabilities, tagClient, tagCache, @@ -39,9 +39,7 @@ export const getBulkActions = ({ if (capabilities.assign && assignableTypes.length > 0) { actions.push( getBulkAssignAction({ - notifications, - overlays, - theme, + ...startServices, tagCache, assignmentService, assignableTypes, @@ -50,7 +48,7 @@ export const getBulkActions = ({ ); } if (capabilities.delete) { - actions.push(getBulkDeleteAction({ notifications, overlays, tagClient, setLoading })); + actions.push(getBulkDeleteAction({ ...startServices, tagClient, setLoading })); } // only add clear selection if user has permission to perform any other action diff --git a/x-pack/plugins/saved_objects_tagging/public/management/mount_section.tsx b/x-pack/plugins/saved_objects_tagging/public/management/mount_section.tsx index 72ac1863075f7..7c55d95a07637 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/mount_section.tsx +++ b/x-pack/plugins/saved_objects_tagging/public/management/mount_section.tsx @@ -7,9 +7,8 @@ import React, { FC } from 'react'; import ReactDOM from 'react-dom'; -import { I18nProvider } from '@kbn/i18n-react'; import { CoreSetup, ApplicationStart } from '@kbn/core/public'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { ManagementAppMountParams } from '@kbn/management-plugin/public'; import { getTagsCapabilities } from '../../common'; import { SavedObjectTaggingPluginStart } from '../types'; @@ -45,27 +44,25 @@ export const mountSection = async ({ title, }: MountSectionParams) => { const [coreStart] = await core.getStartServices(); - const { element, setBreadcrumbs, theme$ } = mountParams; + const { element, setBreadcrumbs } = mountParams; const capabilities = getTagsCapabilities(coreStart.application.capabilities); const assignableTypes = await assignmentService.getAssignableTypes(); coreStart.chrome.docTitle.change(title); ReactDOM.render( - - - - - - - , + + + + + , element ); diff --git a/x-pack/plugins/saved_objects_tagging/public/management/tag_management_page.tsx b/x-pack/plugins/saved_objects_tagging/public/management/tag_management_page.tsx index 8093d59a29bd2..659861a2a0eac 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/tag_management_page.tsx +++ b/x-pack/plugins/saved_objects_tagging/public/management/tag_management_page.tsx @@ -40,7 +40,7 @@ export const TagManagementPage: FC = ({ capabilities, assignableTypes, }) => { - const { overlays, notifications, application, http, theme } = core; + const { application, http, ...startServices } = core; const [loading, setLoading] = useState(false); const [allTags, setAllTags] = useState([]); const [selectedTags, setSelectedTags] = useState([]); @@ -75,13 +75,13 @@ export const TagManagementPage: FC = ({ }); const createModalOpener = useMemo( - () => getCreateModalOpener({ overlays, theme, tagClient, notifications }), - [overlays, theme, tagClient, notifications] + () => getCreateModalOpener({ ...startServices, tagClient }), + [startServices, tagClient] ); const tableActions = useMemo(() => { return getTableActions({ - core, + startServices, capabilities, tagClient, tagCache, @@ -92,7 +92,7 @@ export const TagManagementPage: FC = ({ canceled$: unmount$, }); }, [ - core, + startServices, capabilities, tagClient, tagCache, @@ -105,7 +105,7 @@ export const TagManagementPage: FC = ({ const bulkActions = useMemo(() => { return getBulkActions({ - core, + startServices, capabilities, tagClient, tagCache, @@ -114,7 +114,7 @@ export const TagManagementPage: FC = ({ assignableTypes, clearSelection: () => setSelectedTags([]), }); - }, [core, capabilities, tagClient, tagCache, assignmentService, assignableTypes]); + }, [startServices, capabilities, tagClient, tagCache, assignmentService, assignableTypes]); useEffect(() => { setBreadcrumbs([ @@ -126,6 +126,8 @@ export const TagManagementPage: FC = ({ ]); }, [setBreadcrumbs]); + const { notifications } = startServices; + const openCreateModal = useCallback(() => { createModalOpener({ onCreate: (createdTag) => { diff --git a/x-pack/plugins/saved_objects_tagging/public/plugin.ts b/x-pack/plugins/saved_objects_tagging/public/plugin.ts index e5ad1dfa5095f..c86841765f6e2 100644 --- a/x-pack/plugins/saved_objects_tagging/public/plugin.ts +++ b/x-pack/plugins/saved_objects_tagging/public/plugin.ts @@ -67,7 +67,7 @@ export class SavedObjectTaggingPlugin return {}; } - public start({ http, application, overlays, theme, analytics, notifications }: CoreStart) { + public start({ http, application, analytics, ...startServices }: CoreStart) { this.tagCache = new TagsCache({ refreshHandler: () => this.tagClient!.getAll({ asSystemRequest: true }), refreshInterval: this.config.cacheRefreshInterval, @@ -87,12 +87,11 @@ export class SavedObjectTaggingPlugin client: this.tagClient, cache: this.tagCache, ui: getUiApi({ + ...startServices, + analytics, cache: this.tagCache, client: this.tagClient, capabilities: getTagsCapabilities(application.capabilities), - overlays, - theme, - notifications, }), }; } diff --git a/x-pack/plugins/saved_objects_tagging/public/types.ts b/x-pack/plugins/saved_objects_tagging/public/types.ts index 9ceaa6c72b9fc..ed29542e4d410 100644 --- a/x-pack/plugins/saved_objects_tagging/public/types.ts +++ b/x-pack/plugins/saved_objects_tagging/public/types.ts @@ -5,6 +5,12 @@ * 2.0. */ +import { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; export type SavedObjectTaggingPluginStart = SavedObjectsTaggingApi; + +export type StartServices = Pick< + CoreStart, + 'overlays' | 'notifications' | 'analytics' | 'i18n' | 'theme' +>; diff --git a/x-pack/plugins/saved_objects_tagging/public/ui_api/components.ts b/x-pack/plugins/saved_objects_tagging/public/ui_api/components.ts index e6a504c49e87d..18bc70c9966d9 100644 --- a/x-pack/plugins/saved_objects_tagging/public/ui_api/components.ts +++ b/x-pack/plugins/saved_objects_tagging/public/ui_api/components.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { NotificationsStart, OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { TagsCapabilities } from '../../common'; import { ITagInternalClient, ITagsCache } from '../services'; @@ -15,25 +14,21 @@ import { getConnectedSavedObjectModalTagSelectorComponent, } from '../components/connected'; import { getCreateModalOpener } from '../components/edition_modal'; +import { StartServices } from '../types'; -export interface GetComponentsOptions { +export interface GetComponentsOptions extends StartServices { capabilities: TagsCapabilities; cache: ITagsCache; - overlays: OverlayStart; - theme: ThemeServiceStart; tagClient: ITagInternalClient; - notifications: NotificationsStart; } export const getComponents = ({ capabilities, cache, - overlays, - theme, tagClient, - notifications, + ...startServices }: GetComponentsOptions): SavedObjectsTaggingApiUiComponent => { - const openCreateModal = getCreateModalOpener({ overlays, theme, tagClient, notifications }); + const openCreateModal = getCreateModalOpener({ ...startServices, tagClient }); return { TagList: getConnectedTagListComponent({ cache }), TagSelector: getConnectedTagSelectorComponent({ cache, capabilities, openCreateModal }), diff --git a/x-pack/plugins/saved_objects_tagging/public/ui_api/index.ts b/x-pack/plugins/saved_objects_tagging/public/ui_api/index.ts index b2dca68d5cc95..d25b04dd1002c 100644 --- a/x-pack/plugins/saved_objects_tagging/public/ui_api/index.ts +++ b/x-pack/plugins/saved_objects_tagging/public/ui_api/index.ts @@ -5,10 +5,10 @@ * 2.0. */ -import type { NotificationsStart, OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import { SavedObjectsTaggingApiUi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { TagsCapabilities } from '../../common'; import { ITagsCache, ITagInternalClient } from '../services'; +import { StartServices } from '../types'; import { getTagIdsFromReferences, updateTagsReferences, @@ -23,30 +23,23 @@ import { buildConvertNameToReference } from './convert_name_to_reference'; import { buildGetTagList } from './get_tag_list'; import { hasTagDecoration } from './has_tag_decoration'; -interface GetUiApiOptions { - overlays: OverlayStart; - theme: ThemeServiceStart; +interface GetUiApiOptions extends StartServices { capabilities: TagsCapabilities; cache: ITagsCache; client: ITagInternalClient; - notifications: NotificationsStart; } export const getUiApi = ({ cache, capabilities, client, - overlays, - theme, - notifications, + ...startServices }: GetUiApiOptions): SavedObjectsTaggingApiUi => { const components = getComponents({ + ...startServices, cache, capabilities, - overlays, - theme, tagClient: client, - notifications, }); const getTagList = buildGetTagList(cache); diff --git a/x-pack/plugins/saved_objects_tagging/tsconfig.json b/x-pack/plugins/saved_objects_tagging/tsconfig.json index 5efcb5533e3b2..b5202917aa4ef 100644 --- a/x-pack/plugins/saved_objects_tagging/tsconfig.json +++ b/x-pack/plugins/saved_objects_tagging/tsconfig.json @@ -13,7 +13,6 @@ "@kbn/usage-collection-plugin", "@kbn/management-plugin", "@kbn/saved-objects-tagging-oss-plugin", - "@kbn/kibana-react-plugin", "@kbn/usage-collection-plugin", "@kbn/features-plugin", "@kbn/security-plugin", @@ -24,6 +23,9 @@ "@kbn/ebt-tools", "@kbn/core-notifications-browser", "@kbn/ui-theme", + "@kbn/react-kibana-context-render", + "@kbn/react-kibana-mount", + "@kbn/core-lifecycle-browser", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/serverless/kibana.jsonc b/x-pack/plugins/serverless/kibana.jsonc index a0adba7f60dfc..1c3d5cef4f7bf 100644 --- a/x-pack/plugins/serverless/kibana.jsonc +++ b/x-pack/plugins/serverless/kibana.jsonc @@ -16,8 +16,6 @@ "cloud" ], "optionalPlugins": [], - "requiredBundles": [ - "kibanaReact", - ] + "requiredBundles": [] } } diff --git a/x-pack/plugins/serverless/public/plugin.tsx b/x-pack/plugins/serverless/public/plugin.tsx index 95a9ff43c4270..451752b28168c 100644 --- a/x-pack/plugins/serverless/public/plugin.tsx +++ b/x-pack/plugins/serverless/public/plugin.tsx @@ -7,8 +7,7 @@ import { InternalChromeStart } from '@kbn/core-chrome-browser-internal'; import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; -import { I18nProvider } from '@kbn/i18n-react'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { ProjectSwitcher, ProjectSwitcherKibanaProvider } from '@kbn/serverless-project-switcher'; import { ProjectType } from '@kbn/serverless-types'; import React from 'react'; @@ -120,13 +119,11 @@ export class ServerlessPlugin currentProjectType: ProjectType ) { ReactDOM.render( - - - - - - - , + + + + + , targetDomElement ); diff --git a/x-pack/plugins/serverless/tsconfig.json b/x-pack/plugins/serverless/tsconfig.json index c487758451852..48931f2a37936 100644 --- a/x-pack/plugins/serverless/tsconfig.json +++ b/x-pack/plugins/serverless/tsconfig.json @@ -16,17 +16,16 @@ "kbn_references": [ "@kbn/config-schema", "@kbn/core", - "@kbn/kibana-react-plugin", "@kbn/serverless-project-switcher", "@kbn/serverless-types", "@kbn/utils", "@kbn/core-chrome-browser", "@kbn/core-chrome-browser-internal", - "@kbn/i18n-react", "@kbn/cloud-plugin", "@kbn/serverless-common-settings", "@kbn/shared-ux-chrome-navigation", "@kbn/i18n", "@kbn/management-cards-navigation", + "@kbn/react-kibana-context-render", ] } From cf429235f9db43f266c126007a71e70c11606d6e Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 17 Apr 2024 16:14:53 +0100 Subject: [PATCH 10/36] [ML] Removing tech preview badge for pattern analysis (#181020) **Discover** image **ML** image --- .../log_categorization/log_categorization_for_flyout.tsx | 4 ---- .../ml/public/application/aiops/log_categorization.tsx | 4 ---- 2 files changed, 8 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx index 31a34e0e3655a..b4bcf95129b2b 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx @@ -47,7 +47,6 @@ import type { EventRate } from './use_categorize_request'; import { CategoryTable } from './category_table'; import { InformationText } from './information_text'; import { SamplingMenu } from './sampling_menu'; -import { TechnicalPreviewBadge } from './technical_preview_badge'; import { LoadingCategorization } from './loading_categorization'; import { useValidateFieldRequest } from './use_validate_category_field'; import { FieldValidationCallout } from './category_validation_callout'; @@ -296,9 +295,6 @@ export const LogCategorizationFlyout: FC = ({

- - - forceRefresh()} /> diff --git a/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx b/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx index d3dfb0af1bf43..1ca0fc8ec2ef4 100644 --- a/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx +++ b/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx @@ -15,7 +15,6 @@ import { useDataSource } from '../contexts/ml/data_source_context'; import { useMlKibana } from '../contexts/kibana'; import { useEnabledFeatures } from '../contexts/ml'; import { HelpMenu } from '../components/help_menu'; -import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; import { MlPageHeader } from '../components/page_header'; export const LogCategorizationPage: FC = () => { @@ -34,9 +33,6 @@ export const LogCategorizationPage: FC = () => { defaultMessage="Log pattern analysis" /> - - - {dataView && ( From 3c74a59ea7b3d855d112f032d07992e0c82bca15 Mon Sep 17 00:00:00 2001 From: Elastic Machine Date: Wed, 17 Apr 2024 17:47:43 +0200 Subject: [PATCH 11/36] [main] Sync bundled packages with Package Storage (#181054) Automated by https://buildkite.com/elastic/package-storage-infra-kibana-discover-release-branches/builds/578 --- fleet_packages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fleet_packages.json b/fleet_packages.json index 1c747b4c53ddc..633fd76e2498f 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -34,7 +34,7 @@ }, { "name": "endpoint", - "version": "8.13.0" + "version": "8.14.0" }, { "name": "fleet_server", From 9daed6ed6db734f570d0cfafbeb828b163fbedee Mon Sep 17 00:00:00 2001 From: Joe McElroy Date: Wed, 17 Apr 2024 16:57:00 +0100 Subject: [PATCH 12/36] [Search] [Playground] Fix source fields issue (#180920) Fixes bug where: - adjusting the query fields in the view query flyout wasn't applying them - saving query fields picked into state. Query fields are resetted when the indices are changed. - the elasticsearch query and source fields were not being correctly chosen when indices were changed. This is due this process being async. Now introduced a loading step before the user can move next - adjusting the context fields not saving --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../edit_context/edit_context_flyout.test.tsx | 59 ++++++ .../edit_context/edit_context_flyout.tsx | 29 ++- .../sources_panel/add_indices_field.tsx | 3 + .../sources_panel/create_index_callout.tsx | 1 + .../components/sources_panel/indices_list.tsx | 1 + .../sources_panel/indices_table.tsx | 1 + .../sources_panel_for_sidebar.test.tsx | 73 +++++++ .../sources_panel_for_start_chat.test.tsx | 160 ++++++++------- .../sources_panel_for_start_chat.tsx | 77 ++----- .../sources_panel/sources_panel_sidebar.tsx | 31 +-- .../public/components/start_new_chat.tsx | 4 +- .../view_query/view_query_flyout.test.tsx | 59 ++++++ .../view_query/view_query_flyout.tsx | 52 +++-- .../public/hooks/use_indices_fields.ts | 2 +- .../public/hooks/use_source_indices_field.ts | 104 +++++++++- .../hooks/use_source_indices_fields.test.tsx | 190 ++++++++++++++++++ .../plugins/search_playground/public/types.ts | 2 + .../public/utils/create_query.ts | 16 +- 18 files changed, 658 insertions(+), 206 deletions(-) create mode 100644 x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.test.tsx create mode 100644 x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_sidebar.test.tsx create mode 100644 x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.test.tsx create mode 100644 x-pack/plugins/search_playground/public/hooks/use_source_indices_fields.test.tsx diff --git a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.test.tsx b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.test.tsx new file mode 100644 index 0000000000000..dbffad015e97c --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.test.tsx @@ -0,0 +1,59 @@ +/* + * 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 React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import { EditContextFlyout } from './edit_context_flyout'; +import { FormProvider, useForm } from 'react-hook-form'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +jest.mock('../../hooks/use_indices_fields', () => ({ + useIndicesFields: () => ({ + fields: { + index1: { + elser_query_fields: [], + dense_vector_query_fields: [], + bm25_query_fields: ['field1', 'field2'], + source_fields: ['context_field1', 'context_field2'], + }, + }, + }), +})); + +const MockFormProvider = ({ children }: { children: React.ReactElement }) => { + const methods = useForm({ + values: { + indices: ['index1'], + }, + }); + return {children}; +}; + +describe('EditContextFlyout component tests', () => { + const onCloseMock = jest.fn(); + + beforeEach(() => { + render( + + + + + + ); + }); + + it('calls onClose when the close button is clicked', () => { + fireEvent.click(screen.getByTestId('euiFlyoutCloseButton')); + expect(onCloseMock).toHaveBeenCalledTimes(1); + }); + + it('should see the context fields', async () => { + expect(screen.getByTestId('contextFieldsSelectable')).toBeInTheDocument(); + expect(screen.getByTestId('contextFieldsSelectable')).toHaveTextContent(`context_field2`); + expect(screen.getByTestId('contextFieldsSelectable')).toHaveTextContent(`context_field1`); + }); +}); diff --git a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.tsx b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.tsx index 96371c7f5ec95..86c5781fd7f90 100644 --- a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.tsx +++ b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useState } from 'react'; import { EuiFlyout, EuiFlyoutHeader, @@ -35,11 +35,10 @@ interface EditContextFlyoutProps { } export const EditContextFlyout: React.FC = ({ onClose }) => { - const { watch } = useFormContext(); - const selectedIndices: string[] = watch('indices'); - const { fields } = useIndicesFields(selectedIndices || []); - const defaultFields = useMemo(() => getDefaultSourceFields(fields), [fields]); - const [sourceFields, setSourceFields] = useState(defaultFields); + const { getValues } = useFormContext(); + const selectedIndices: string[] = getValues(ChatFormFields.indices); + const { fields } = useIndicesFields(selectedIndices); + const defaultFields = getDefaultSourceFields(fields); const { field: { onChange: onChangeSize, value: docSizeInitialValue }, @@ -50,26 +49,23 @@ export const EditContextFlyout: React.FC = ({ onClose }) const [docSize, setDocSize] = useState(docSizeInitialValue); const { - field: { onChange: onChangeSourceFields }, + field: { onChange: onChangeSourceFields, value: sourceFields }, } = useController({ name: ChatFormFields.sourceFields, + defaultValue: defaultFields, }); - useEffect(() => { - if (selectedIndices?.length > 0) { - setSourceFields(defaultFields); - } - }, [selectedIndices, defaultFields]); + const [tempSourceFields, setTempSourceFields] = useState(sourceFields); const toggleSourceField = (index: string, f: EuiSelectableOption[]) => { - setSourceFields({ - ...sourceFields, + setTempSourceFields({ + ...tempSourceFields, [index]: f.filter(({ checked }) => checked === 'on').map(({ label }) => label), }); }; const saveSourceFields = () => { - onChangeSourceFields(sourceFields); + onChangeSourceFields(tempSourceFields); onChangeSize(docSize); onClose(); }; @@ -148,9 +144,10 @@ export const EditContextFlyout: React.FC = ({ onClose }) ({ label: field, - checked: sourceFields[index]?.includes(field) ? 'on' : undefined, + checked: tempSourceFields[index]?.includes(field) ? 'on' : undefined, }))} onChange={(newOptions) => toggleSourceField(index, newOptions)} listProps={{ bordered: false }} diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/add_indices_field.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/add_indices_field.tsx index c05923afd72d1..c43937ad1614d 100644 --- a/x-pack/plugins/search_playground/public/components/sources_panel/add_indices_field.tsx +++ b/x-pack/plugins/search_playground/public/components/sources_panel/add_indices_field.tsx @@ -15,11 +15,13 @@ import { useQueryIndices } from '../../hooks/use_query_indices'; interface AddIndicesFieldProps { selectedIndices: IndexName[]; onIndexSelect: (index: IndexName) => void; + loading: boolean; } export const AddIndicesField: React.FC = ({ selectedIndices, onIndexSelect, + loading, }) => { const [query, setQuery] = useState(''); const { indices, isLoading } = useQueryIndices(query); @@ -50,6 +52,7 @@ export const AddIndicesField: React.FC = ({ onChange={handleChange} onSearchChange={handleSearchChange} fullWidth + isDisabled={loading} options={indices.map((index) => ({ label: index, disabled: selectedIndices.includes(index), diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.tsx index 1698570a8d221..a7ab1746e78ea 100644 --- a/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.tsx +++ b/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.tsx @@ -34,6 +34,7 @@ export const CreateIndexCallout: React.FC = () => { })} color="primary" iconType="iInCircle" + data-test-subj="createIndexCallout" >

diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/indices_list.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/indices_list.tsx index b127c1ecd8419..7af94bbdce814 100644 --- a/x-pack/plugins/search_playground/public/components/sources_panel/indices_list.tsx +++ b/x-pack/plugins/search_playground/public/components/sources_panel/indices_list.tsx @@ -41,6 +41,7 @@ export const IndicesList: React.FC = ({ indices, onRemoveClick iconType: 'minusInCircle', onClick: () => onRemoveClick(index), disabled: indices.length === 1, + 'data-test-subj': `removeIndexButton`, }} /> ))} diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/indices_table.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/indices_table.tsx index 85f53eec44be4..50dcee6c283bf 100644 --- a/x-pack/plugins/search_playground/public/components/sources_panel/indices_table.tsx +++ b/x-pack/plugins/search_playground/public/components/sources_panel/indices_table.tsx @@ -41,6 +41,7 @@ export const IndicesTable: React.FC = ({ indices, onRemoveCli ), icon: 'minusInCircle', onClick: (item: { index: string }) => onRemoveClick(item.index), + 'data-test-subj': `removeIndexButton`, }, ], }, diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_sidebar.test.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_sidebar.test.tsx new file mode 100644 index 0000000000000..cb5b2fb78eb56 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_sidebar.test.tsx @@ -0,0 +1,73 @@ +/* + * 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 React from 'react'; +import { render, screen } from '@testing-library/react'; +import { SourcesPanelSidebar } from './sources_panel_sidebar'; +import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; +import { useQueryIndices } from '../../hooks/use_query_indices'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +jest.mock('../../hooks/use_source_indices_field', () => ({ + useSourceIndicesFields: jest.fn(), +})); +jest.mock('../../hooks/use_query_indices', () => ({ + useQueryIndices: jest.fn(), +})); + +const Wrapper: React.FC = ({ children }) => { + return ( + <> + {children} + + ); +}; + +describe('SourcesPanelSidebar component', () => { + afterEach(jest.clearAllMocks); + + it('shows the "AddIndicesField" component when there are indices and not loading', () => { + (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); + (useSourceIndicesFields as jest.Mock).mockReturnValue({ + indices: [], + removeIndex: jest.fn(), + addIndex: jest.fn(), + loading: false, + }); + + render(, { wrapper: Wrapper }); + expect(screen.queryByTestId('indicesLoading')).not.toBeInTheDocument(); + }); + + it('displays IndicesTable when there are selected indices', () => { + (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); + (useSourceIndicesFields as jest.Mock).mockReturnValue({ + indices: ['index1', 'index2'], + removeIndex: jest.fn(), + addIndex: jest.fn(), + loading: false, + }); + + render(, { wrapper: Wrapper }); + expect(screen.getAllByTestId('removeIndexButton')).toHaveLength(2); + expect(screen.getAllByTestId('removeIndexButton')[0]).not.toBeDisabled(); + expect(screen.getAllByTestId('removeIndexButton')[1]).not.toBeDisabled(); + }); + + it('does not allow to remove all indices', () => { + (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); + (useSourceIndicesFields as jest.Mock).mockReturnValue({ + indices: ['index1'], + removeIndex: jest.fn(), + addIndex: jest.fn(), + loading: false, + }); + + render(, { wrapper: Wrapper }); + expect(screen.getByTestId('removeIndexButton')).toBeDisabled(); + }); +}); diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.test.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.test.tsx index 8d066168ceefd..9195f8295e735 100644 --- a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.test.tsx +++ b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.test.tsx @@ -6,99 +6,103 @@ */ import React from 'react'; -import { render as testingLibraryRender, screen } from '@testing-library/react'; -import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; - +import { render, screen } from '@testing-library/react'; import { SourcesPanelForStartChat } from './sources_panel_for_start_chat'; -import { useSourceIndicesField } from '../../hooks/use_source_indices_field'; -import { getDefaultSourceFields } from '../../utils/create_query'; +import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; +import { useQueryIndices } from '../../hooks/use_query_indices'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -const render = (children: React.ReactNode) => - testingLibraryRender({children}); +jest.mock('../../hooks/use_source_indices_field', () => ({ + useSourceIndicesFields: jest.fn(), +})); +jest.mock('../../hooks/use_query_indices', () => ({ + useQueryIndices: jest.fn(), +})); -jest.mock('./create_index_callout', () => ({ CreateIndexCallout: () => 'mocked component' })); -jest.mock('../../hooks/use_source_indices_field'); -jest.mock('../../utils/create_query'); -jest.mock('../../hooks/use_query_indices', () => { - return { - useQueryIndices: () => { - return { - indices: [], - isLoading: false, - }; - }, - }; -}); -jest.mock('../../hooks/use_indices_fields', () => { - return { - useIndicesFields: () => { - return { - fields: {}, - isLoading: false, - }; +jest.mock('../../hooks/use_kibana', () => ({ + useKibana: jest.fn(() => ({ + services: { + application: { navigateToUrl: jest.fn() }, + share: { url: { locators: { get: jest.fn() } } }, }, - }; -}); -jest.mock('react-hook-form', () => { - return { - useController: () => { - return { - field: { onChange: jest.fn() }, - }; - }, - }; -}); -const mockUseSourceIndicesField = useSourceIndicesField as jest.Mock; -const mockGetDefaultSourceFields = getDefaultSourceFields as jest.Mock; + })), +})); -describe('SourcesPanelForStartChat', () => { - describe('renders sources', () => { - beforeEach(() => { - mockUseSourceIndicesField.mockReturnValue({ - selectedIndices: [], - addIndex: jest.fn(), - removeIndex: jest.fn(), - }); - mockGetDefaultSourceFields.mockReturnValue({}); - }); +const Wrapper: React.FC = ({ children }) => { + return ( + <> + {children} + + ); +}; + +describe('SourcesPanelForStartChat component', () => { + afterEach(jest.clearAllMocks); - test('renders Sources', () => { - render(); - expect(screen.getByText(/Select Sources/i)).toBeInTheDocument(); + it('shows a loading spinner when query is loading', () => { + (useQueryIndices as jest.Mock).mockReturnValue({ indices: [], isLoading: true }); + (useSourceIndicesFields as jest.Mock).mockReturnValue({ + indices: [], + removeIndex: jest.fn(), + addIndex: jest.fn(), + loading: false, }); + + render(, { wrapper: Wrapper }); + expect(screen.getByTestId('indicesLoading')).toBeInTheDocument(); }); - describe('with default index', () => { - beforeEach(() => { - mockUseSourceIndicesField.mockReturnValue({ - selectedIndices: ['index-1'], - addIndex: jest.fn(), - removeIndex: jest.fn(), - }); - mockGetDefaultSourceFields.mockReturnValue({ - 'index-1': ['text'], - }); + it('shows the "AddIndicesField" component when there are indices and not loading', () => { + (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); + (useSourceIndicesFields as jest.Mock).mockReturnValue({ + indices: [], + removeIndex: jest.fn(), + addIndex: jest.fn(), + loading: false, }); - test('renders Sources', () => { - render(); - expect(screen.getByText(/Select Sources/i)).toBeInTheDocument(); - }); - test('renders indices table', () => { - render(); - expect(screen.getByText(/index-1/i)).toBeInTheDocument(); + render(, { wrapper: Wrapper }); + expect(screen.queryByTestId('indicesLoading')).not.toBeInTheDocument(); + }); + + it('displays IndicesTable when there are selected indices', () => { + (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); + (useSourceIndicesFields as jest.Mock).mockReturnValue({ + indices: ['index1'], + removeIndex: jest.fn(), + addIndex: jest.fn(), + loading: false, }); + + render(, { wrapper: Wrapper }); + expect(screen.getAllByText('index1')).toHaveLength(1); + expect(screen.getByTestId('removeIndexButton')).toBeInTheDocument(); }); - describe('no source fields', () => { - beforeEach(() => { - mockGetDefaultSourceFields.mockReturnValue({ - 'index-1': [undefined], - }); + it('displays "CreateIndexCallout" when no indices are found and not loading', () => { + (useQueryIndices as jest.Mock).mockReturnValue({ indices: [], isLoading: false }); + (useSourceIndicesFields as jest.Mock).mockReturnValue({ + indices: [], + removeIndex: jest.fn(), + addIndex: jest.fn(), + loading: false, }); - test('renders warning callout', () => { - render(); - expect(screen.getByText(/No source fields found for index-1/i)).toBeInTheDocument(); + + render(, { wrapper: Wrapper }); + expect(screen.getByTestId('createIndexCallout')).toBeInTheDocument(); + }); + + it('renders warning callout', () => { + (useSourceIndicesFields as jest.Mock).mockReturnValue({ + indices: ['index1'], + removeIndex: jest.fn(), + addIndex: jest.fn(), + loading: false, + noFieldsIndicesWarning: 'index1', }); + + render(); + expect(screen.getByTestId('NoIndicesFieldsMessage')).toBeInTheDocument(); + expect(screen.getByTestId('NoIndicesFieldsMessage')).toHaveTextContent('index1'); }); }); diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.tsx index 1cab207291993..9773918ae0ccc 100644 --- a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.tsx +++ b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.tsx @@ -5,65 +5,25 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiCallOut } from '@elastic/eui'; -import React, { useState, useEffect } from 'react'; +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import React from 'react'; import { i18n } from '@kbn/i18n'; -import { useController } from 'react-hook-form'; import { AddIndicesField } from './add_indices_field'; import { IndicesTable } from './indices_table'; import { StartChatPanel } from '../start_chat_panel'; import { CreateIndexCallout } from './create_index_callout'; -import { useSourceIndicesField } from '../../hooks/use_source_indices_field'; import { useQueryIndices } from '../../hooks/use_query_indices'; -import { ChatFormFields } from '../../types'; -import { useIndicesFields } from '../../hooks/use_indices_fields'; -import { - createQuery, - getDefaultQueryFields, - getDefaultSourceFields, - IndexFields, -} from '../../utils/create_query'; - -const transformToErrorMessage = (defaultSourceFields: IndexFields): string | undefined => { - const indices: string[] = []; - Object.keys(defaultSourceFields).forEach((index: string) => { - if (defaultSourceFields[index][0] === undefined) { - indices.push(index); - } - }); - - return indices.length === 0 ? undefined : indices.join(); -}; +import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; export const SourcesPanelForStartChat: React.FC = () => { - const { selectedIndices, removeIndex, addIndex } = useSourceIndicesField(); - const { indices, isLoading } = useQueryIndices(); - const { fields } = useIndicesFields(selectedIndices || []); - const [sourceFieldErrorMessage, setSourceFieldErrorMessage] = useState(); - const { - field: { onChange: elasticsearchQueryOnChange }, - } = useController({ - name: ChatFormFields.elasticsearchQuery, - defaultValue: {}, - }); - - const { - field: { onChange: sourceFieldsOnChange }, - } = useController({ - name: ChatFormFields.sourceFields, - defaultValue: {}, - }); - - useEffect(() => { - if (fields) { - const defaultFields = getDefaultQueryFields(fields); - elasticsearchQueryOnChange(createQuery(defaultFields, fields)); - const defaultSourceFields = getDefaultSourceFields(fields); - sourceFieldsOnChange(defaultSourceFields); - setSourceFieldErrorMessage(transformToErrorMessage(defaultSourceFields)); - } - }, [fields, elasticsearchQueryOnChange, sourceFieldsOnChange]); + indices: selectedIndices, + removeIndex, + addIndex, + loading: fieldIndicesLoading, + noFieldsIndicesWarning, + } = useSourceIndicesFields(); + const { indices, isLoading } = useQueryIndices(); return ( { )} - {sourceFieldErrorMessage && ( - + {noFieldsIndicesWarning && ( +

{i18n.translate('xpack.searchPlayground.emptyPrompts.sources.warningCallout', { - defaultMessage: 'No source fields found for {errorMessage}', + defaultMessage: + 'No fields found for {errorMessage}. Try adding data to these indices.', values: { - errorMessage: sourceFieldErrorMessage, + errorMessage: noFieldsIndicesWarning, }, })}

@@ -96,13 +57,17 @@ export const SourcesPanelForStartChat: React.FC = () => { {isLoading && ( - + )} {!isLoading && !!indices?.length && ( - + )} diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_sidebar.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_sidebar.tsx index 76a68605c9b0a..d62af60d0e89c 100644 --- a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_sidebar.tsx +++ b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_sidebar.tsx @@ -5,34 +5,15 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { useController } from 'react-hook-form'; -import { useSourceIndicesField } from '../../hooks/use_source_indices_field'; -import { useIndicesFields } from '../../hooks/use_indices_fields'; -import { createQuery, getDefaultQueryFields } from '../../utils/create_query'; -import { ChatFormFields } from '../../types'; +import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; import { AddIndicesField } from './add_indices_field'; import { IndicesList } from './indices_list'; export const SourcesPanelSidebar: React.FC = () => { - const { selectedIndices, removeIndex, addIndex } = useSourceIndicesField(); - const { fields } = useIndicesFields(selectedIndices || []); - - const { - field: { onChange: elasticsearchQueryOnChange }, - } = useController({ - name: ChatFormFields.elasticsearchQuery, - defaultValue: {}, - }); - - useEffect(() => { - if (fields) { - const defaultFields = getDefaultQueryFields(fields); - elasticsearchQueryOnChange(createQuery(defaultFields, fields)); - } - }, [selectedIndices, fields, elasticsearchQueryOnChange]); + const { indices: selectedIndices, removeIndex, addIndex, loading } = useSourceIndicesFields(); return ( @@ -51,7 +32,11 @@ export const SourcesPanelSidebar: React.FC = () => {
- + ); diff --git a/x-pack/plugins/search_playground/public/components/start_new_chat.tsx b/x-pack/plugins/search_playground/public/components/start_new_chat.tsx index 981d55f5aab28..1a84b0e86655e 100644 --- a/x-pack/plugins/search_playground/public/components/start_new_chat.tsx +++ b/x-pack/plugins/search_playground/public/components/start_new_chat.tsx @@ -66,7 +66,9 @@ export const StartNewChat: React.FC = ({ onStartClick }) => { iconType="arrowRight" iconSide="right" disabled={ - !watch(ChatFormFields.indices, []).length || !Object.keys(connectors || {}).length + !watch(ChatFormFields.indices, []).length || + !Object.keys(connectors || {}).length || + !watch(ChatFormFields.elasticsearchQuery, '') } onClick={onStartClick} > diff --git a/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.test.tsx b/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.test.tsx new file mode 100644 index 0000000000000..af70223743981 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.test.tsx @@ -0,0 +1,59 @@ +/* + * 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 React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import { ViewQueryFlyout } from './view_query_flyout'; +import { FormProvider, useForm } from 'react-hook-form'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +jest.mock('../../hooks/use_indices_fields', () => ({ + useIndicesFields: () => ({ + fields: { + index1: { + elser_query_fields: [], + dense_vector_query_fields: [], + bm25_query_fields: ['field1', 'field2'], + }, + }, + }), +})); + +const MockFormProvider = ({ children }: { children: React.ReactElement }) => { + const methods = useForm({ + values: { + indices: ['index1'], + }, + }); + return {children}; +}; + +describe('ViewQueryFlyout component tests', () => { + const onCloseMock = jest.fn(); + + beforeEach(() => { + render( + + + + + + ); + }); + + it('calls onClose when the close button is clicked', () => { + fireEvent.click(screen.getByTestId('euiFlyoutCloseButton')); + expect(onCloseMock).toHaveBeenCalledTimes(1); + }); + + it('should see the view elasticsearch query', async () => { + expect(screen.getByTestId('ViewElasticsearchQueryResult')).toBeInTheDocument(); + expect(screen.getByTestId('ViewElasticsearchQueryResult')).toHaveTextContent( + `{ "retriever": { "standard": { "query": { "multi_match": { "query": "{query}", "fields": [ "field1" ] } } } } }` + ); + }); +}); diff --git a/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.tsx b/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.tsx index f0181794702ba..63b6c8cc2a9ff 100644 --- a/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.tsx +++ b/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.tsx @@ -24,8 +24,9 @@ import { EuiTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useEffect, useMemo, useState } from 'react'; -import { useController, useFormContext } from 'react-hook-form'; +import React, { useState } from 'react'; +import { useFormContext } from 'react-hook-form'; +import { useController } from 'react-hook-form'; import { useIndicesFields } from '../../hooks/use_indices_fields'; import { ChatForm, ChatFormFields } from '../../types'; import { createQuery, getDefaultQueryFields } from '../../utils/create_query'; @@ -35,42 +36,43 @@ interface ViewQueryFlyoutProps { } export const ViewQueryFlyout: React.FC = ({ onClose }) => { - const { watch } = useFormContext(); - const selectedIndices: string[] = watch(ChatFormFields.indices); - const { fields } = useIndicesFields(selectedIndices || []); - const defaultFields = useMemo(() => getDefaultQueryFields(fields), [fields]); - const [queryFields, setQueryFields] = useState(defaultFields); + const { getValues } = useFormContext(); + const selectedIndices: string[] = getValues(ChatFormFields.indices); + const { fields } = useIndicesFields(selectedIndices); + const defaultFields = getDefaultQueryFields(fields); const { - field: { onChange }, + field: { onChange: queryFieldsOnChange, value: queryFields }, } = useController({ - name: ChatFormFields.elasticsearchQuery, - defaultValue: {}, + name: ChatFormFields.queryFields, + defaultValue: defaultFields, }); - useEffect(() => { - if (selectedIndices?.length > 0) { - setQueryFields(defaultFields); - } - }, [selectedIndices, defaultFields]); + const [tempQueryFields, setTempQueryFields] = useState(queryFields); + + const { + field: { onChange: elasticsearchQueryChange }, + } = useController({ + name: ChatFormFields.elasticsearchQuery, + }); const isQueryFieldSelected = (index: string, field: string) => { - return queryFields[index].includes(field); + return tempQueryFields[index].includes(field); }; const updateFields = (index: string, options: EuiSelectableOption[]) => { const newFields = options .filter((option) => option.checked === 'on') .map((option) => option.label); - setQueryFields({ - ...queryFields, + setTempQueryFields({ + ...tempQueryFields, [index]: newFields, }); }; const saveQuery = () => { - onChange(createQuery(queryFields, fields)); - + queryFieldsOnChange(tempQueryFields); + elasticsearchQueryChange(createQuery(tempQueryFields, fields)); onClose(); }; @@ -99,8 +101,14 @@ export const ViewQueryFlyout: React.FC = ({ onClose }) => - - {JSON.stringify(createQuery(queryFields, fields), null, 2)} + + {JSON.stringify(createQuery(tempQueryFields, fields), null, 2)} diff --git a/x-pack/plugins/search_playground/public/hooks/use_indices_fields.ts b/x-pack/plugins/search_playground/public/hooks/use_indices_fields.ts index 1e0dc14850836..f78ea3accf206 100644 --- a/x-pack/plugins/search_playground/public/hooks/use_indices_fields.ts +++ b/x-pack/plugins/search_playground/public/hooks/use_indices_fields.ts @@ -9,7 +9,7 @@ import { useQuery } from '@tanstack/react-query'; import { useKibana } from './use_kibana'; import { APIRoutes, IndicesQuerySourceFields } from '../types'; -export const useIndicesFields = (indices: string[]) => { +export const useIndicesFields = (indices: string[] = []) => { const { services } = useKibana(); const { data, isLoading } = useQuery({ diff --git a/x-pack/plugins/search_playground/public/hooks/use_source_indices_field.ts b/x-pack/plugins/search_playground/public/hooks/use_source_indices_field.ts index 1192c475d36c5..93ec081dffef6 100644 --- a/x-pack/plugins/search_playground/public/hooks/use_source_indices_field.ts +++ b/x-pack/plugins/search_playground/public/hooks/use_source_indices_field.ts @@ -5,24 +5,112 @@ * 2.0. */ +import { useQuery } from '@tanstack/react-query'; +import { useController, useFormContext } from 'react-hook-form'; import { IndexName } from '@elastic/elasticsearch/lib/api/types'; -import { useController } from 'react-hook-form'; -import { ChatFormFields } from '../types'; +import { useEffect, useState } from 'react'; +import { useKibana } from './use_kibana'; +import { APIRoutes, IndicesQuerySourceFields } from '../types'; +import { ChatForm, ChatFormFields } from '../types'; +import { + createQuery, + getDefaultQueryFields, + getDefaultSourceFields, + IndexFields, +} from '../utils/create_query'; + +export const getIndicesWithNoSourceFields = ( + defaultSourceFields: IndexFields +): string | undefined => { + const indices: string[] = []; + Object.keys(defaultSourceFields).forEach((index: string) => { + if (defaultSourceFields[index].length === 0) { + indices.push(index); + } + }); + + return indices.length === 0 ? undefined : indices.join(); +}; + +export const useSourceIndicesFields = () => { + const { services } = useKibana(); + const [loading, setLoading] = useState(false); + const [noFieldsIndicesWarning, setNoFieldsIndicesWarning] = useState(null); + const { resetField } = useFormContext(); + + const { + field: { value: selectedIndices, onChange: onIndicesChange }, + } = useController({ + name: ChatFormFields.indices, + }); + + const { + field: { onChange: onElasticsearchQueryChange }, + } = useController({ + name: ChatFormFields.elasticsearchQuery, + defaultValue: {}, + }); -export const useSourceIndicesField = () => { const { - field: { value: selectedIndices, onChange }, - } = useController({ name: ChatFormFields.indices, defaultValue: [] }); + field: { onChange: onSourceFieldsChange }, + } = useController({ + name: ChatFormFields.sourceFields, + }); + + const { data: fields } = useQuery({ + enabled: selectedIndices.length > 0, + queryKey: ['fields', selectedIndices.toString()], + queryFn: async () => { + const response = await services.http.post( + APIRoutes.POST_QUERY_SOURCE_FIELDS, + { + body: JSON.stringify({ indices: selectedIndices }), + } + ); + return response; + }, + }); + + useEffect(() => { + if (fields) { + resetField(ChatFormFields.queryFields); + + const defaultFields = getDefaultQueryFields(fields); + const defaultSourceFields = getDefaultSourceFields(fields); + + const indicesWithNoSourceFields = getIndicesWithNoSourceFields(defaultSourceFields); + + if (indicesWithNoSourceFields) { + setNoFieldsIndicesWarning(indicesWithNoSourceFields); + } else { + setNoFieldsIndicesWarning(null); + } + + onElasticsearchQueryChange(createQuery(defaultFields, fields)); + onSourceFieldsChange(defaultSourceFields); + } + setLoading(false); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fields]); + const addIndex = (newIndex: IndexName) => { - onChange([...selectedIndices, newIndex]); + const newIndices = [...selectedIndices, newIndex]; + setLoading(true); + onIndicesChange(newIndices); }; + const removeIndex = (index: IndexName) => { - onChange(selectedIndices.filter((indexName: string) => indexName !== index)); + const newIndices = selectedIndices.filter((indexName: string) => indexName !== index); + setLoading(true); + onIndicesChange(newIndices); }; return { - selectedIndices, + indices: selectedIndices, + fields, + loading, addIndex, removeIndex, + noFieldsIndicesWarning, }; }; diff --git a/x-pack/plugins/search_playground/public/hooks/use_source_indices_fields.test.tsx b/x-pack/plugins/search_playground/public/hooks/use_source_indices_fields.test.tsx new file mode 100644 index 0000000000000..754002f21d568 --- /dev/null +++ b/x-pack/plugins/search_playground/public/hooks/use_source_indices_fields.test.tsx @@ -0,0 +1,190 @@ +/* + * 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 { renderHook, act } from '@testing-library/react-hooks'; +import { useKibana } from './use_kibana'; +import { PlaygroundProvider } from '../providers/playground_provider'; +import React from 'react'; +import * as ReactHookForm from 'react-hook-form'; + +jest.mock('./use_kibana', () => ({ + useKibana: jest.fn(), +})); + +let formHookSpy: jest.SpyInstance; + +import { getIndicesWithNoSourceFields, useSourceIndicesFields } from './use_source_indices_field'; +import { IndicesQuerySourceFields } from '../types'; + +describe('useSourceIndicesFields Hook', () => { + let postMock: jest.Mock; + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + beforeEach(() => { + formHookSpy = jest.spyOn(ReactHookForm, 'useForm'); + const querySourceFields: IndicesQuerySourceFields = { + newIndex: { + elser_query_fields: [ + { + field: 'field1', + model_id: 'model1', + nested: false, + indices: ['newIndex'], + }, + ], + dense_vector_query_fields: [], + bm25_query_fields: [], + source_fields: ['field1'], + }, + }; + + postMock = jest.fn().mockResolvedValue(querySourceFields); + (useKibana as jest.Mock).mockImplementation(() => ({ + services: { + http: { + post: postMock, + }, + }, + })); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('getIndicesWithNoSourceFields', () => { + it('should return undefined if all indices have source fields', () => { + const defaultSourceFields = { + index1: ['field1'], + index2: ['field2'], + }; + expect(getIndicesWithNoSourceFields(defaultSourceFields)).toBeUndefined(); + }); + + it('should return indices with no source fields', () => { + const defaultSourceFields = { + index1: ['field1'], + index2: [], + }; + expect(getIndicesWithNoSourceFields(defaultSourceFields)).toBe('index2'); + }); + }); + + it('should handle addIndex correctly changing indices and updating loading state', async () => { + const { result, waitForNextUpdate } = renderHook(() => useSourceIndicesFields(), { wrapper }); + const { getValues } = formHookSpy.mock.results[0].value; + + act(() => { + expect(result.current.indices).toEqual([]); + expect(getValues()).toMatchInlineSnapshot(` + Object { + "doc_size": 5, + "elasticsearch_query": Object {}, + "indices": Array [], + "prompt": "You are an assistant for question-answering tasks.", + "source_fields": Array [], + } + `); + result.current.addIndex('newIndex'); + }); + + await act(async () => { + await waitForNextUpdate(); + expect(result.current.indices).toEqual(['newIndex']); + expect(result.current.loading).toBe(true); + }); + + expect(postMock).toHaveBeenCalled(); + + await act(async () => { + expect(result.current.loading).toBe(false); + expect(getValues()).toMatchInlineSnapshot(` + Object { + "doc_size": 5, + "elasticsearch_query": Object { + "retriever": Object { + "standard": Object { + "query": Object { + "text_expansion": Object { + "field1": Object { + "model_id": "model1", + "model_text": "{query}", + }, + }, + }, + }, + }, + }, + "indices": Array [ + "newIndex", + ], + "prompt": "You are an assistant for question-answering tasks.", + "source_fields": Object { + "newIndex": Array [ + "field1", + ], + }, + } + `); + }); + }); + + it('should provide warning message for adding an index without any fields', async () => { + const querySourceFields: IndicesQuerySourceFields = { + missing_fields_index: { + elser_query_fields: [], + dense_vector_query_fields: [], + bm25_query_fields: [], + source_fields: [], + }, + }; + + postMock.mockResolvedValue(querySourceFields); + + const { result, waitForNextUpdate } = renderHook(() => useSourceIndicesFields(), { wrapper }); + const { getValues } = formHookSpy.mock.results[0].value; + + await act(async () => { + result.current.addIndex('missing_fields_index'); + }); + + await act(async () => { + await waitForNextUpdate(); + }); + + expect(postMock).toHaveBeenCalled(); + + await act(async () => { + expect(result.current.noFieldsIndicesWarning).toEqual('missing_fields_index'); + expect(result.current.loading).toBe(false); + expect(getValues()).toMatchInlineSnapshot(` + Object { + "doc_size": 5, + "elasticsearch_query": Object { + "retriever": Object { + "standard": Object { + "query": Object { + "match_all": Object {}, + }, + }, + }, + }, + "indices": Array [ + "missing_fields_index", + ], + "prompt": "You are an assistant for question-answering tasks.", + "source_fields": Object { + "missing_fields_index": Array [], + }, + } + `); + }); + }); +}); diff --git a/x-pack/plugins/search_playground/public/types.ts b/x-pack/plugins/search_playground/public/types.ts index 74a28a2eb8d1e..f3079bc56bb06 100644 --- a/x-pack/plugins/search_playground/public/types.ts +++ b/x-pack/plugins/search_playground/public/types.ts @@ -56,6 +56,7 @@ export enum ChatFormFields { summarizationModel = 'summarization_model', sourceFields = 'source_fields', docSize = 'doc_size', + queryFields = 'query_fields', } export interface ChatForm { @@ -67,6 +68,7 @@ export interface ChatForm { [ChatFormFields.elasticsearchQuery]: { query: QueryDslQueryContainer }; [ChatFormFields.sourceFields]: string[]; [ChatFormFields.docSize]: number; + [ChatFormFields.queryFields]: { [index: string]: string[] }; } export enum MessageRole { diff --git a/x-pack/plugins/search_playground/public/utils/create_query.ts b/x-pack/plugins/search_playground/public/utils/create_query.ts index 0dbcfd873b571..7d7419e53cc22 100644 --- a/x-pack/plugins/search_playground/public/utils/create_query.ts +++ b/x-pack/plugins/search_playground/public/utils/create_query.ts @@ -27,7 +27,13 @@ const SUGGESTED_BM25_FIELDS = [ const SUGGESTED_DENSE_VECTOR_FIELDS = ['content_vector.tokens']; -const SUGGESTED_SOURCE_FIELDS = ['body_content', 'content', 'text', 'page_content_text']; +const SUGGESTED_SOURCE_FIELDS = [ + 'body_content', + 'content', + 'text', + 'page_content_text', + 'text_field', +]; interface Matches { queryMatches: any[]; @@ -216,6 +222,14 @@ export function getDefaultSourceFields(fieldDescriptors: IndicesQuerySourceField (acc: IndexFields, index: string) => { const indexFieldDescriptors = fieldDescriptors[index]; + // if there are no source fields, we don't need to suggest anything + if (indexFieldDescriptors.source_fields.length === 0) { + return { + ...acc, + [index]: [], + }; + } + const suggested = indexFieldDescriptors.source_fields.filter((x) => SUGGESTED_SOURCE_FIELDS.includes(x) ); From 4d79b2773e19c9f254dd00148b9c7c11d8064e14 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 17 Apr 2024 17:15:08 +0100 Subject: [PATCH 13/36] skip flaky suite (#180994) --- .../observability/dataset_quality/dataset_quality_flyout.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts index ad17189d83803..29b78d25cf168 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts @@ -30,7 +30,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const to = '2024-01-01T12:00:00.000Z'; - describe('Dataset quality flyout', () => { + // FLAKY: https://github.com/elastic/kibana/issues/180994 + describe.skip('Dataset quality flyout', () => { before(async () => { await PageObjects.svlCommonPage.loginWithRole('admin'); await synthtrace.index(getInitialTestLogs({ to, count: 4 })); From 7cc93b33cc95409173edc71e8e9f4c5c8eccde92 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 17 Apr 2024 17:16:52 +0100 Subject: [PATCH 14/36] skip flaky suite (#170985) --- .../e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts index 4e0ae2080a97b..03ff413405b53 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts @@ -23,7 +23,8 @@ import { ensurePolicyDetailsPageAuthzAccess, } from '../../../screens'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/170985 +describe.skip( 'Roles for Security Essential PLI with Endpoint Essentials addon', { tags: ['@serverless'], From 600825ab22126640860849d928dfc2cdae6b9c9d Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 17 Apr 2024 17:18:04 +0100 Subject: [PATCH 15/36] skip flaky suite (#181008) --- .../cypress/e2e/explore/urls/compatibility.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/compatibility.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/compatibility.cy.ts index 1a13708e0d779..36f72fe0e3871 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/compatibility.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/compatibility.cy.ts @@ -32,7 +32,8 @@ const ABSOLUTE_DATE = { const RULE_ID = '5a4a0460-d822-11eb-8962-bfd4aff0a9b3'; -describe('URL compatibility', { tags: ['@ess', '@skipInServerless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/181008 +describe.skip('URL compatibility', { tags: ['@ess', '@skipInServerless'] }, () => { beforeEach(() => { login(); }); From 51fd2d5c365ef9f827e2a97b507e042d75d0d84e Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Wed, 17 Apr 2024 10:16:53 -0700 Subject: [PATCH 16/36] [Oblt UX: Infrastructure and Services] Remove usage of deprecated React rendering utilities (#180844) ## Summary Partially addresses https://github.com/elastic/kibana-team/issues/805 Follows https://github.com/elastic/kibana/pull/180003 These changes come up from searching in the code and finding where certain kinds of deprecated AppEx-SharedUX modules are imported. **Reviewers: Please interact with critical paths through the UI components touched in this PR, ESPECIALLY in terms of testing dark mode and i18n.** This focuses on code within Observability User Experience: Infrastructure and Services. image ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../public/alerts/lib/alerts_toast.tsx | 16 +- .../application/hooks/use_alerts_modal.ts | 7 +- .../hooks/use_request_error_handler.tsx | 13 +- .../monitoring/public/application/index.tsx | 500 +++++++++--------- .../application/pages/access_denied/index.tsx | 4 +- .../elasticsearch/elasticsearch_template.tsx | 5 +- .../elasticsearch/ingest_pipeline_modal.tsx | 15 +- .../components/cluster/listing/listing.js | 23 +- x-pack/plugins/monitoring/tsconfig.json | 2 + .../observability_solution/apm/tsconfig.json | 2 +- .../public/application/index.tsx | 76 ++- .../public/application/types.ts | 4 + .../contexts/exploratory_view_config.tsx | 9 +- .../header/add_to_case_action.tsx | 8 +- .../hooks/use_lens_attributes.test.tsx | 3 +- .../obsv_exploratory_view.tsx | 4 +- .../shared/exploratory_view/rtl_helpers.tsx | 1 + .../exploratory_view/public/routes/index.tsx | 5 +- .../exploratory_view/tsconfig.json | 4 +- .../profiling/public/app.tsx | 7 +- .../profiling/tsconfig.json | 1 + .../getting_started/use_simple_monitor.ts | 7 +- .../hooks/use_monitor_save.tsx | 14 +- .../monitor_list_table/delete_monitor.tsx | 15 +- .../management/show_sync_errors.tsx | 12 +- .../settings/global_params/delete_param.tsx | 14 +- .../browser_test_results.tsx | 8 +- .../simple_test_results.tsx | 9 +- .../state/monitor_list/toast_title.tsx | 6 +- .../public/apps/synthetics/synthetics_app.tsx | 151 +++--- .../synthetics/public/kibana_services.ts | 4 + .../synthetics/tsconfig.json | 5 +- .../public/legacy_uptime/app/uptime_app.tsx | 124 +++-- .../monitor/ml/ml_flyout_container.tsx | 14 +- .../lib/alert_types/alert_messages.tsx | 12 +- .../legacy_uptime/state/alerts/alerts.ts | 2 +- .../uptime/tsconfig.json | 5 +- .../ux/public/application/ux_app.tsx | 82 +-- .../app/rum_dashboard/utils/test_helper.tsx | 34 +- .../observability_solution/ux/tsconfig.json | 4 +- 40 files changed, 623 insertions(+), 608 deletions(-) diff --git a/x-pack/plugins/monitoring/public/alerts/lib/alerts_toast.tsx b/x-pack/plugins/monitoring/public/alerts/lib/alerts_toast.tsx index 4c2ed3f50c4e0..3cef2c1722438 100644 --- a/x-pack/plugins/monitoring/public/alerts/lib/alerts_toast.tsx +++ b/x-pack/plugins/monitoring/public/alerts/lib/alerts_toast.tsx @@ -8,17 +8,16 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiLink } from '@elastic/eui'; -import type { Observable } from 'rxjs'; -import type { CoreTheme } from '@kbn/core/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { Legacy } from '../../legacy_shims'; +import { MonitoringStartServices } from '../../types'; export interface EnableAlertResponse { isSufficientlySecure?: boolean; hasPermanentEncryptionKey?: boolean; } -const showApiKeyAndEncryptionError = (theme$?: Observable) => { +const showApiKeyAndEncryptionError = (services: MonitoringStartServices) => { const settingsUrl = Legacy.shims.docLinks.links.alerting.generalSettings; Legacy.shims.toastNotifications.addWarning({ @@ -40,7 +39,7 @@ const showApiKeyAndEncryptionError = (theme$?: Observable) => { })} , - { theme$ } + services ), }); }; @@ -58,11 +57,14 @@ const showAlertsCreatedConfirmation = () => { }); }; -export const showAlertsToast = (response: EnableAlertResponse, theme$?: Observable) => { +export const showAlertsToast = ( + response: EnableAlertResponse, + services: MonitoringStartServices +) => { const { isSufficientlySecure, hasPermanentEncryptionKey } = response; if (isSufficientlySecure === false || hasPermanentEncryptionKey === false) { - showApiKeyAndEncryptionError(theme$); + showApiKeyAndEncryptionError(services); } else { showAlertsCreatedConfirmation(); } diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_alerts_modal.ts b/x-pack/plugins/monitoring/public/application/hooks/use_alerts_modal.ts index 5469d02895bfc..c7c3295d44be1 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_alerts_modal.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_alerts_modal.ts @@ -7,9 +7,10 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useRequestErrorHandler } from './use_request_error_handler'; import { EnableAlertResponse, showAlertsToast } from '../../alerts/lib/alerts_toast'; +import { MonitoringStartServices } from '../../types'; export const useAlertsModal = () => { - const { services } = useKibana(); + const { services } = useKibana(); const handleRequestError = useRequestErrorHandler(); function shouldShowAlertsModal(alerts: {}) { @@ -38,9 +39,9 @@ export const useAlertsModal = () => { {} )!; window.localStorage.setItem('ALERTS_MODAL_DECISION_MADE', 'true'); - showAlertsToast(response, services.theme?.theme$); + showAlertsToast(response, services); } catch (err) { - await handleRequestError(err); + handleRequestError(err); } } diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_request_error_handler.tsx b/x-pack/plugins/monitoring/public/application/hooks/use_request_error_handler.tsx index e5a3e4260ac2f..11f152536b54e 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_request_error_handler.tsx +++ b/x-pack/plugins/monitoring/public/application/hooks/use_request_error_handler.tsx @@ -11,9 +11,10 @@ import { includes } from 'lodash'; import type { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; -import { toMountPoint, useKibana } from '@kbn/kibana-react-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { formatMsg } from '../../lib/format_msg'; -import { MonitoringStartPluginDependencies } from '../../types'; +import { MonitoringStartServices } from '../../types'; export function formatMonitoringError(err: IHttpFetchError) { if (err.response?.status && err.response?.status !== -1) { @@ -35,7 +36,7 @@ export function formatMonitoringError(err: IHttpFetchError) { } export const useRequestErrorHandler = () => { - const { services } = useKibana(); + const { services } = useKibana(); const history = useHistory(); return useCallback( (err: IHttpFetchError) => { @@ -64,7 +65,7 @@ export const useRequestErrorHandler = () => { /> , - { theme$: services.theme?.theme$ } + services ), }); } else { @@ -72,10 +73,10 @@ export const useRequestErrorHandler = () => { title: i18n.translate('xpack.monitoring.ajaxErrorHandler.requestErrorNotificationTitle', { defaultMessage: 'Monitoring Request Error', }), - text: toMountPoint(formatMonitoringError(err), { theme$: services.theme?.theme$ }), + text: toMountPoint(formatMonitoringError(err), services), }); } }, - [history, services.notifications?.toasts, services.theme] + [history, services] ); }; diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx index b9fd25f60c367..39969c550570e 100644 --- a/x-pack/plugins/monitoring/public/application/index.tsx +++ b/x-pack/plugins/monitoring/public/application/index.tsx @@ -6,8 +6,8 @@ */ import { AppMountParameters, CoreStart, CoreTheme, MountPoint } from '@kbn/core/public'; -import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import React from 'react'; import ReactDOM from 'react-dom'; import { Redirect } from 'react-router-dom'; @@ -21,7 +21,7 @@ import { CODE_PATH_KIBANA, CODE_PATH_LOGSTASH, } from '../../common/constants'; -import { MonitoringStartPluginDependencies } from '../types'; +import { MonitoringStartPluginDependencies, MonitoringStartServices } from '../types'; import { ExternalConfig, ExternalConfigContext } from './contexts/external_config_context'; import { GlobalStateProvider } from './contexts/global_state_context'; import { HeaderActionMenuContext } from './contexts/header_action_menu_context'; @@ -99,255 +99,253 @@ const MonitoringApp: React.FC<{ theme$: Observable; }> = ({ core, plugins, externalConfig, setHeaderActionMenu, theme$ }) => { const history = createPreserveQueryHistory(); - const darkMode = core.theme.getTheme().darkMode; + const startServices: MonitoringStartServices = { ...core, ...plugins }; return ( - - - - - - - - - - - - - - - - - - {/* ElasticSearch Views */} - - - - - - - - - - - - - - - - - - - - - {/* Kibana Views */} - - - - - - - {/* Beats Views */} - - - - - - - {/* Logstash Routes */} - - - - - - - - - - - - - - - {/* APM Views */} - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + {/* ElasticSearch Views */} + + + + + + + + + + + + + + + + + + + + + {/* Kibana Views */} + + + + + + + {/* Beats Views */} + + + + + + + {/* Logstash Routes */} + + + + + + + + + + + + + + + {/* APM Views */} + + + + + + + + + + + + + + + + + + ); }; diff --git a/x-pack/plugins/monitoring/public/application/pages/access_denied/index.tsx b/x-pack/plugins/monitoring/public/application/pages/access_denied/index.tsx index 2ad779588b6af..839cf418123fb 100644 --- a/x-pack/plugins/monitoring/public/application/pages/access_denied/index.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/access_denied/index.tsx @@ -13,12 +13,12 @@ import useInterval from 'react-use/lib/useInterval'; import { Redirect } from 'react-router-dom'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ComponentProps } from '../../route_init'; -import { MonitoringStartPluginDependencies } from '../../../types'; +import { MonitoringStartServices } from '../../../types'; import { ExternalConfigContext } from '../../contexts/external_config_context'; export const AccessDeniedPage: React.FC = () => { const { isCcsEnabled } = useContext(ExternalConfigContext); - const { services } = useKibana(); + const { services } = useKibana(); const [hasAccess, setHasAccess] = useState(false); useInterval(() => { diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/elasticsearch_template.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/elasticsearch_template.tsx index b034478f720c3..a1d2fc6ca2fe3 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/elasticsearch_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/elasticsearch_template.tsx @@ -8,9 +8,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { includes } from 'lodash'; -import { DashboardStart } from '@kbn/dashboard-plugin/public'; -import { FleetStart } from '@kbn/fleet-plugin/public'; import { EuiIcon, EuiToolTip } from '@elastic/eui'; +import { MonitoringStartServices } from '../../../types'; import { PageTemplate } from '../page_template'; import { TabMenuItem, PageTemplateProps } from '../page_template'; import { ML_SUPPORTED_LICENSES } from '../../../../common/constants'; @@ -24,7 +23,7 @@ export const ElasticsearchTemplate: React.FC = ({ cluster, ...props }) => { - const { services } = useKibana<{ dashboard?: DashboardStart; fleet?: FleetStart }>(); + const { services } = useKibana(); const tabs: TabMenuItem[] = [ { diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ingest_pipeline_modal.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ingest_pipeline_modal.tsx index 29207177d93ec..a1b3e2805e8b5 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ingest_pipeline_modal.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ingest_pipeline_modal.tsx @@ -6,13 +6,12 @@ */ import React, { useState } from 'react'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import type { CoreStart } from '@kbn/core/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { EuiCallOut, EuiConfirmModal, EuiSpacer } from '@elastic/eui'; -import { DashboardStart } from '@kbn/dashboard-plugin/public'; -import { FleetStart, KibanaSavedObjectType } from '@kbn/fleet-plugin/public'; +import { KibanaSavedObjectType } from '@kbn/fleet-plugin/public'; +import { MonitoringStartServices } from '../../../types'; const INGEST_PIPELINE_DASHBOARD_ID = 'elasticsearch-metrics-ingest-pipelines'; @@ -22,9 +21,7 @@ const INGEST_PIPELINE_DASHBOARD_ID = 'elasticsearch-metrics-ingest-pipelines'; * @param services * @returns */ -export const ingestPipelineTabOnClick = async ( - services: Partial -) => { +export const ingestPipelineTabOnClick = async (services: MonitoringStartServices) => { const response = await services.fleet?.hooks.epm.getBulkAssets({ assetIds: [ { @@ -56,9 +53,7 @@ export const ingestPipelineTabOnClick = async ( canInstallPackages={!!services.fleet?.authz.integrations.installPackages} closeModal={() => ref.close()} />, - { - theme$: services.theme?.theme$, - } + services ) ); diff --git a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js index d4bc3e3529610..a10dbd1fd2198 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js @@ -28,7 +28,8 @@ import { AlertsStatus } from '../../../alerts/status'; import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import './listing.scss'; -import { toMountPoint, useKibana } from '@kbn/kibana-react-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; const IsClusterSupported = ({ isSupported, children }) => { return isSupported ? children : '-'; @@ -260,7 +261,7 @@ const licenseWarning = (scope, { title, text }) => { }); }; -const handleClickIncompatibleLicense = (scope, theme$, clusterName) => { +const handleClickIncompatibleLicense = (scope, services, clusterName) => { licenseWarning(scope, { title: i18n.translate( 'xpack.monitoring.cluster.listing.incompatibleLicense.warningMessageTitle', @@ -294,12 +295,12 @@ const handleClickIncompatibleLicense = (scope, theme$, clusterName) => { />

, - { theme$ } + services ), }); }; -const handleClickInvalidLicense = (scope, theme$, clusterName) => { +const handleClickInvalidLicense = (scope, services, clusterName) => { const licensingPath = `${Legacy.shims.getBasePath()}/app/management/stack/license_management/home`; licenseWarning(scope, { @@ -340,7 +341,7 @@ const handleClickInvalidLicense = (scope, theme$, clusterName) => { />

, - { theme$ } + services ), }); }; @@ -404,16 +405,8 @@ export const Listing = ({ angular, clusters, sorting, pagination, onTableChange const { services } = useKibana(); const _changeCluster = partial(changeCluster, scope, globalState); - const _handleClickIncompatibleLicense = partial( - handleClickIncompatibleLicense, - scope, - services.theme.theme$ - ); - const _handleClickInvalidLicense = partial( - handleClickInvalidLicense, - scope, - services.theme.theme$ - ); + const _handleClickIncompatibleLicense = partial(handleClickIncompatibleLicense, scope, services); + const _handleClickInvalidLicense = partial(handleClickInvalidLicense, scope, services); const hasStandaloneCluster = !!clusters.find( (cluster) => cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID ); diff --git a/x-pack/plugins/monitoring/tsconfig.json b/x-pack/plugins/monitoring/tsconfig.json index 112bf5f695b17..6361f5beea6f0 100644 --- a/x-pack/plugins/monitoring/tsconfig.json +++ b/x-pack/plugins/monitoring/tsconfig.json @@ -44,6 +44,8 @@ "@kbn/logs-shared-plugin", "@kbn/alerts-as-data-utils", "@kbn/rule-data-utils", + "@kbn/react-kibana-mount", + "@kbn/react-kibana-context-render", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/observability_solution/apm/tsconfig.json b/x-pack/plugins/observability_solution/apm/tsconfig.json index 0eaae3f970a31..1f1fd6c1c954a 100644 --- a/x-pack/plugins/observability_solution/apm/tsconfig.json +++ b/x-pack/plugins/observability_solution/apm/tsconfig.json @@ -113,7 +113,7 @@ "@kbn/shared-ux-utility", "@kbn/management-settings-components-field-row", "@kbn/shared-ux-markdown", - "@kbn/core-http-request-handler-context-server" + "@kbn/core-http-request-handler-context-server", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/application/index.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/application/index.tsx index d496af2b23fb9..cda4cda9d8618 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/application/index.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/application/index.tsx @@ -5,14 +5,14 @@ * 2.0. */ -import { EuiErrorBoundary } from '@elastic/eui'; import React from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; import { Router, Routes, Route } from '@kbn/shared-ux-router'; import { AppMountParameters, APP_WRAPPER_CLASS, CoreStart } from '@kbn/core/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; @@ -20,7 +20,9 @@ import { PluginContext } from '../context/plugin_context'; import { routes } from '../routes'; import { ExploratoryViewPublicPluginsStart } from '../plugin'; -function App() { +export type StartServices = Pick; + +function App(props: { startServices: StartServices }) { return ( <> @@ -28,7 +30,7 @@ function App() { const path = key as keyof typeof routes; const { handler, exact } = routes[path]; const Wrapper = () => { - return handler(); + return handler({ startServices: props.startServices }); }; return ; })} @@ -50,8 +52,7 @@ export const renderApp = ({ usageCollection: UsageCollectionSetup; isDev?: boolean; }) => { - const { element, history, theme$ } = appMountParameters; - const i18nCore = core.i18n; + const { element, history } = appMountParameters; const isDarkMode = core.theme.getTheme().darkMode; core.chrome.setHelpExtension({ @@ -68,45 +69,38 @@ export const renderApp = ({ usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; ReactDOM.render( - + - - + - - - - -
- - - -
-
-
-
-
-
-
+ + +
+ + + +
+
+
+ +
-
, + , element ); return () => { diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/application/types.ts b/x-pack/plugins/observability_solution/exploratory_view/public/application/types.ts index e15e86c5f99e1..2311b9c08407f 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/application/types.ts +++ b/x-pack/plugins/observability_solution/exploratory_view/public/application/types.ts @@ -6,10 +6,12 @@ */ import type { + AnalyticsServiceStart, ApplicationStart, ChromeStart, DocLinksStart, HttpStart, + I18nStart, IUiSettingsClient, NotificationsStart, OverlayStart, @@ -48,6 +50,8 @@ export interface ObservabilityAppServices { share: SharePluginStart; stateTransfer: EmbeddableStateTransfer; storage: IStorageWrapper; + analytics: AnalyticsServiceStart; + i18n: I18nStart; theme: ThemeServiceStart; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; uiSettings: IUiSettingsClient; diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/contexts/exploratory_view_config.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/contexts/exploratory_view_config.tsx index 08df78cd072a9..82e72bf29d7a1 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/contexts/exploratory_view_config.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/contexts/exploratory_view_config.tsx @@ -6,13 +6,15 @@ */ import React, { createContext, useContext, useState } from 'react'; -import { AppMountParameters } from '@kbn/core/public'; +import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import type { AppDataType, ConfigProps, ReportViewType, SeriesConfig } from '../types'; export type ReportConfigMap = Record SeriesConfig>>; -interface ExploratoryViewContextValue { +type StartServices = Pick; + +interface ExploratoryViewContextValue extends StartServices { dataTypes: Array<{ id: AppDataType; label: string }>; reportTypes: Array<{ reportType: ReportViewType | typeof SELECT_REPORT_TYPE; @@ -21,6 +23,7 @@ interface ExploratoryViewContextValue { reportConfigMap: ReportConfigMap; asPanel?: boolean; setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; + // FIXME: use theme from CoreStart theme$: AppMountParameters['theme$']; isEditMode?: boolean; setIsEditMode?: React.Dispatch>; @@ -38,6 +41,7 @@ export function ExploratoryViewContextProvider({ setHeaderActionMenu, asPanel = true, theme$, + ...startServices }: { children: JSX.Element } & ExploratoryViewContextValue) { const [isEditMode, setIsEditMode] = useState(false); @@ -50,6 +54,7 @@ export function ExploratoryViewContextProvider({ theme$, isEditMode, setIsEditMode, + ...startServices, }; return ( diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.tsx index 590451eaea6ef..8f91ee3c87cd6 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.tsx @@ -8,7 +8,8 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useCallback, useEffect } from 'react'; -import { toMountPoint, useKibana } from '@kbn/kibana-react-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { CasesDeepLinkId, generateCaseViewPath, @@ -41,7 +42,6 @@ export function AddToCaseAction({ const { cases, application: { getUrlForApp }, - theme, } = kServices; const getToastText = useCallback( @@ -53,9 +53,9 @@ export function AddToCaseAction({ path: generateCaseViewPath({ detailName: theCase.id }), })} />, - { theme$: theme?.theme$ } + kServices ), - [getUrlForApp, theme?.theme$] + [getUrlForApp, kServices] ); const absoluteFromDate = parseRelativeDate(timeRange.from); diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx index 9f98d6b812e75..1581ffd446713 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx @@ -19,7 +19,7 @@ import * as useAppDataViewHook from './use_app_data_view'; import * as theme from '@kbn/observability-shared-plugin/public/hooks/use_theme'; import { dataTypes, obsvReportConfigMap, reportTypesList } from '../obsv_exploratory_view'; import { ExploratoryViewContextProvider } from '../contexts/exploratory_view_config'; -import { themeServiceMock } from '@kbn/core/public/mocks'; +import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; import * as lensHook from './use_lens_formula_helper'; import { lensPluginMock } from '@kbn/lens-plugin/public/mocks'; import { FormulaPublicApi } from '@kbn/lens-plugin/public'; @@ -76,6 +76,7 @@ describe('useExpViewTimeRange', function () { reportConfigMap={obsvReportConfigMap} setHeaderActionMenu={jest.fn()} theme$={themeServiceMock.createTheme$()} + {...coreMock.createStart()} > {children} diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/obsv_exploratory_view.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/obsv_exploratory_view.tsx index 795f6aaaf45a1..55b41a6aee6c8 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/obsv_exploratory_view.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/obsv_exploratory_view.tsx @@ -36,6 +36,7 @@ import { getMobileDeviceDistributionConfig } from './configurations/mobile/devic import { usePluginContext } from '../../../hooks/use_plugin_context'; import { getLogsKPIConfig } from './configurations/infra_logs/kpi_over_time_config'; import { getSingleMetricConfig } from './configurations/rum/single_metric_config'; +import { StartServices } from '../../../application'; export const dataTypes: Array<{ id: AppDataType; label: string }> = [ { @@ -108,7 +109,7 @@ export const obsvReportConfigMap = { [DataTypes.ALERTS]: [getAlertsKPIConfig, getAlertsSingleMetricConfig], }; -export function ObservabilityExploratoryView() { +export function ObservabilityExploratoryView(props: { startServices: StartServices }) { const { appMountParameters } = usePluginContext(); return ( @@ -119,6 +120,7 @@ export function ObservabilityExploratoryView() { reportConfigMap={obsvReportConfigMap} setHeaderActionMenu={appMountParameters.setHeaderActionMenu} theme$={appMountParameters.theme$} + {...props.startServices} > diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/rtl_helpers.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/rtl_helpers.tsx index e89cd6d6d3aff..336c6eae6badb 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/rtl_helpers.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/components/shared/exploratory_view/rtl_helpers.tsx @@ -217,6 +217,7 @@ export function render( reportConfigMap={obsvReportConfigMap} setHeaderActionMenu={jest.fn()} theme$={themeServiceMock.createTheme$()} + {...defaultCore} > {ui} diff --git a/x-pack/plugins/observability_solution/exploratory_view/public/routes/index.tsx b/x-pack/plugins/observability_solution/exploratory_view/public/routes/index.tsx index 9ce0d5ec42662..0a7f2d54fad40 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/public/routes/index.tsx +++ b/x-pack/plugins/observability_solution/exploratory_view/public/routes/index.tsx @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import React from 'react'; import { jsonRt } from './json_rt'; import { ObservabilityExploratoryView } from '../components/shared/exploratory_view/obsv_exploratory_view'; +import { StartServices } from '../application'; export type RouteParams = DecodeParams; @@ -23,8 +24,8 @@ export interface Params { export const routes = { '/': { - handler: () => { - return ; + handler: ({ startServices }: { startServices: StartServices }) => { + return ; }, params: { query: t.partial({ diff --git a/x-pack/plugins/observability_solution/exploratory_view/tsconfig.json b/x-pack/plugins/observability_solution/exploratory_view/tsconfig.json index cdfeb258831a2..87002a070158a 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/tsconfig.json +++ b/x-pack/plugins/observability_solution/exploratory_view/tsconfig.json @@ -39,7 +39,9 @@ "@kbn/observability-shared-plugin", "@kbn/core-ui-settings-browser-mocks", "@kbn/observability-ai-assistant-plugin", - "@kbn/shared-ux-link-redirect-app" + "@kbn/shared-ux-link-redirect-app", + "@kbn/react-kibana-context-render", + "@kbn/react-kibana-mount" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/profiling/public/app.tsx b/x-pack/plugins/observability_solution/profiling/public/app.tsx index f90c14ac7044f..0b4cfbaaaa44c 100644 --- a/x-pack/plugins/observability_solution/profiling/public/app.tsx +++ b/x-pack/plugins/observability_solution/profiling/public/app.tsx @@ -6,7 +6,8 @@ */ import { AppMountParameters, CoreSetup, CoreStart } from '@kbn/core/public'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; @@ -85,7 +86,7 @@ function App({ }, [coreStart, coreSetup, pluginsStart, pluginsSetup, profilingFetchServices]); return ( - + @@ -117,7 +118,7 @@ function App({ - + ); } diff --git a/x-pack/plugins/observability_solution/profiling/tsconfig.json b/x-pack/plugins/observability_solution/profiling/tsconfig.json index fc9080606e454..1f18459455e7e 100644 --- a/x-pack/plugins/observability_solution/profiling/tsconfig.json +++ b/x-pack/plugins/observability_solution/profiling/tsconfig.json @@ -54,6 +54,7 @@ "@kbn/shared-ux-utility", "@kbn/management-settings-components-field-row", "@kbn/deeplinks-observability", + "@kbn/react-kibana-context-render", "@kbn/apm-data-access-plugin" // add references to other TypeScript projects the plugin depends on diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts index d014891f53465..e4015386ff333 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts @@ -56,23 +56,24 @@ export const useSimpleMonitor = ({ monitorData }: { monitorData?: SimpleFormData }, [monitorData]); useEffect(() => { + const { core, toasts } = kibanaService; const newMonitor = data as UpsertMonitorResponse; const hasErrors = data && 'attributes' in data && data.attributes.errors?.length > 0; if (hasErrors && !loading) { showSyncErrors( (data as { attributes: { errors: ServiceLocationErrors } })?.attributes.errors ?? [], serviceLocations, - kibanaService.toasts + core ); } if (!loading && status === FETCH_STATUS.FAILURE) { - kibanaService.toasts.addDanger({ + toasts.addDanger({ title: MONITOR_FAILURE_LABEL, toastLifeTimeMs: 3000, }); } else if (!loading && (newMonitor as EncryptedSyntheticsSavedMonitor)?.id) { - kibanaService.toasts.addSuccess({ + toasts.addSuccess({ title: MONITOR_SUCCESS_LABEL, toastLifeTimeMs: 3000, }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx index ae847d9be069f..4fa482eb63bbd 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx @@ -6,7 +6,7 @@ */ import { FETCH_STATUS, useFetcher } from '@kbn/observability-shared-plugin/public'; -import { toMountPoint, useKibana } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { useParams, useRouteMatch } from 'react-router-dom'; import React, { useEffect } from 'react'; import { useDispatch } from 'react-redux'; @@ -19,8 +19,6 @@ import { cleanMonitorListState, IHttpSerializedFetchError } from '../../../state import { useSyntheticsRefreshContext } from '../../../contexts'; export const useMonitorSave = ({ monitorData }: { monitorData?: SyntheticsMonitor }) => { - const core = useKibana(); - const theme$ = core.services.theme?.theme$; const dispatch = useDispatch(); const { refreshApp } = useSyntheticsRefreshContext(); const { monitorId } = useParams<{ monitorId: string }>(); @@ -44,8 +42,10 @@ export const useMonitorSave = ({ monitorData }: { monitorData?: SyntheticsMonito }, [monitorData]); useEffect(() => { + const { core, toasts } = kibanaService; + if (status === FETCH_STATUS.FAILURE && error) { - kibanaService.toasts.addError( + toasts.addError( { ...error, message: (error as unknown as IHttpSerializedFetchError).body.message ?? error.message, @@ -55,18 +55,18 @@ export const useMonitorSave = ({ monitorData }: { monitorData?: SyntheticsMonito } else if (status === FETCH_STATUS.SUCCESS && !loading) { refreshApp(); dispatch(cleanMonitorListState()); - kibanaService.toasts.addSuccess({ + toasts.addSuccess({ title: monitorId ? MONITOR_UPDATED_SUCCESS_LABEL : MONITOR_SUCCESS_LABEL, text: toMountPoint(

{monitorId ? MONITOR_UPDATED_SUCCESS_LABEL_SUBTEXT : MONITOR_SUCCESS_LABEL_SUBTEXT}

, - { theme$ } + core ), toastLifeTimeMs: 3000, }); } - }, [data, status, monitorId, loading, refreshApp, dispatch, theme$, error]); + }, [data, status, monitorId, loading, refreshApp, dispatch, error]); return { status, loading, isEdit }; }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx index da830b69e4f7d..b16829fa5880a 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useState } from 'react'; import { EuiCallOut, EuiConfirmModal, EuiLink, EuiSpacer } from '@elastic/eui'; import { FETCH_STATUS, useFetcher } from '@kbn/observability-shared-plugin/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -42,21 +42,25 @@ export const DeleteMonitor = ({ }, [configId, isDeleting]); useEffect(() => { + const { core, toasts } = kibanaService; if (!isDeleting) { return; } if (monitorDeleteStatus === FETCH_STATUS.FAILURE) { - kibanaService.toasts.addDanger( + toasts.addDanger( { title: toMountPoint( -

{labels.MONITOR_DELETE_FAILURE_LABEL}

+

+ {labels.MONITOR_DELETE_FAILURE_LABEL} +

, + core ), }, { toastLifeTimeMs: 3000 } ); } else if (monitorDeleteStatus === FETCH_STATUS.SUCCESS) { reloadPage(); - kibanaService.toasts.addSuccess( + toasts.addSuccess( { title: toMountPoint(

@@ -67,7 +71,8 @@ export const DeleteMonitor = ({ values: { name }, } )} -

+

, + core ), }, { toastLifeTimeMs: 3000 } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/show_sync_errors.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/show_sync_errors.tsx index ee048593e5de1..477639b7774a4 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/show_sync_errors.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/show_sync_errors.tsx @@ -7,18 +7,19 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import { IToasts } from '@kbn/core/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { CoreStart } from '@kbn/core/public'; import { ServiceLocationErrors, ServiceLocations } from '../../../../../../common/runtime_types'; export const showSyncErrors = ( errors: ServiceLocationErrors, locations: ServiceLocations, - toasts: IToasts + core: CoreStart ) => { + const { notifications, ...startServices } = core; Object.values(errors).forEach((location) => { const { status: responseStatus, reason } = location.error || {}; - toasts.addWarning({ + notifications.toasts.addWarning({ title: i18n.translate('xpack.synthetics.monitorManagement.service.error.title', { defaultMessage: `Unable to sync monitor config`, }), @@ -48,7 +49,8 @@ export const showSyncErrors = ( : null}

) : null} - + , + startServices ), toastLifeTimeMs: 30000, }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx index 942afb91cfe48..f2e86f077123d 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useState } from 'react'; import { EuiConfirmModal } from '@elastic/eui'; import { FETCH_STATUS, useFetcher } from '@kbn/observability-shared-plugin/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { i18n } from '@kbn/i18n'; import { useDispatch } from 'react-redux'; @@ -48,8 +48,10 @@ export const DeleteParam = ({ if (!isDeleting) { return; } + const { core, toasts } = kibanaService; + if (status === FETCH_STATUS.FAILURE) { - kibanaService.toasts.addDanger( + toasts.addDanger( { title: toMountPoint(

@@ -58,13 +60,14 @@ export const DeleteParam = ({ defaultMessage: 'Param {name} failed to delete.', values: { name }, })} -

+

, + core ), }, { toastLifeTimeMs: 3000 } ); } else if (status === FETCH_STATUS.SUCCESS) { - kibanaService.toasts.addSuccess( + toasts.addSuccess( { title: toMountPoint(

@@ -72,7 +75,8 @@ export const DeleteParam = ({ defaultMessage: 'Param {name} deleted successfully.', values: { name }, })} -

+

, + core ), }, { toastLifeTimeMs: 3000 } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/browser_test_results.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/browser_test_results.tsx index 5710ad7faaa42..326e981d2c4e2 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/browser_test_results.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/browser_test_results.tsx @@ -7,7 +7,7 @@ import React, { Fragment, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { FormattedMessage } from '@kbn/i18n-react'; import { kibanaService } from '../../../../../utils/kibana_service'; import { useBrowserRunOnceMonitors } from '../hooks/use_browser_run_once_monitors'; @@ -38,8 +38,9 @@ export const BrowserTestRunResult = ({ }); useEffect(() => { + const { core, toasts } = kibanaService; if (retriesExceeded) { - kibanaService.toasts.addDanger( + toasts.addDanger( { text: FAILED_TO_SCHEDULE, title: toMountPoint( @@ -47,7 +48,8 @@ export const BrowserTestRunResult = ({ id="xpack.synthetics.manualTestRun.failedTest.name" defaultMessage="Manual test run failed for {name}" values={{ name }} - /> + />, + core ), }, { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/simple_test_results.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/simple_test_results.tsx index 97dcd4cdd2147..f88b6b3483a43 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/simple_test_results.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/simple_test_results.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { FAILED_TO_SCHEDULE } from './browser_test_results'; import { kibanaService } from '../../../../../utils/kibana_service'; import { useSimpleRunOnceMonitors } from '../hooks/use_simple_run_once_monitors'; @@ -25,7 +25,9 @@ export function SimpleTestResults({ name, testRunId, expectPings, onDone }: Prop useEffect(() => { if (retriesExceeded) { - kibanaService.toasts.addDanger( + const { core, toasts } = kibanaService; + + toasts.addDanger( { text: FAILED_TO_SCHEDULE, title: toMountPoint( @@ -33,7 +35,8 @@ export function SimpleTestResults({ name, testRunId, expectPings, onDone }: Prop id="xpack.synthetics.manualTestRun.failedTest.name" defaultMessage="Manual test run failed for {name}" values={{ name }} - /> + />, + core ), }, { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/toast_title.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/toast_title.tsx index 0ff25d5773732..0083a5a75339f 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/toast_title.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/toast_title.tsx @@ -5,9 +5,11 @@ * 2.0. */ -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import React from 'react'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { kibanaService } from '../../../../utils/kibana_service'; + export function toastTitle({ title, testAttribute }: { title: string; testAttribute?: string }) { - return toMountPoint(

{title}

); + return toMountPoint(

{title}

, kibanaService.core); } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/synthetics_app.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/synthetics_app.tsx index 9288395290fda..93c45442695ad 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/synthetics_app.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/synthetics_app.tsx @@ -4,31 +4,33 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import React, { useEffect } from 'react'; import { Provider as ReduxProvider } from 'react-redux'; -import { Router } from '@kbn/shared-ux-router'; -import { EuiErrorBoundary } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; + import { APP_WRAPPER_CLASS } from '@kbn/core/public'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import { i18n } from '@kbn/i18n'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { InspectorContextProvider } from '@kbn/observability-shared-plugin/public'; -import { SyntheticsDataViewContextProvider } from './contexts/synthetics_data_view_context'; -import { SyntheticsAppProps } from './contexts'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; +import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import { Router } from '@kbn/shared-ux-router'; +import { kibanaService } from '../../utils/kibana_service'; +import { ActionMenu } from './components/common/header/action_menu'; +import { TestNowModeFlyoutContainer } from './components/test_now_mode/test_now_mode_flyout_container'; import { + SyntheticsAppProps, SyntheticsRefreshContextProvider, SyntheticsSettingsContextProvider, - SyntheticsThemeContextProvider, SyntheticsStartupPluginsContextProvider, + SyntheticsThemeContextProvider, } from './contexts'; - +import { SyntheticsDataViewContextProvider } from './contexts/synthetics_data_view_context'; import { PageRouter } from './routes'; -import { store, storage, setBasePath } from './state'; -import { kibanaService } from '../../utils/kibana_service'; -import { ActionMenu } from './components/common/header/action_menu'; -import { TestNowModeFlyoutContainer } from './components/test_now_mode/test_now_mode_flyout_container'; +import { setBasePath, storage, store } from './state'; const Application = (props: SyntheticsAppProps) => { const { @@ -36,7 +38,6 @@ const Application = (props: SyntheticsAppProps) => { canSave, core, darkMode, - i18n: i18nCore, plugins, renderGlobalHelpControls, setBadge, @@ -68,68 +69,66 @@ const Application = (props: SyntheticsAppProps) => { store.dispatch(setBasePath(basePath)); return ( - - - - - - - - - - - - -
- - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
+ + + + + + + + + + + +
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
); }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/kibana_services.ts b/x-pack/plugins/observability_solution/synthetics/public/kibana_services.ts index eb125eb87c744..9c3f641c29a80 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/kibana_services.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/kibana_services.ts @@ -7,9 +7,13 @@ import type { CoreStart } from '@kbn/core/public'; +type StartServices = Pick; + let coreStart: CoreStart; export function setStartServices(core: CoreStart) { coreStart = core; } +export const getStartServices = (): StartServices => coreStart; + export const getDocLinks = () => coreStart?.docLinks; diff --git a/x-pack/plugins/observability_solution/synthetics/tsconfig.json b/x-pack/plugins/observability_solution/synthetics/tsconfig.json index 5a552084919fe..71b989fe671a6 100644 --- a/x-pack/plugins/observability_solution/synthetics/tsconfig.json +++ b/x-pack/plugins/observability_solution/synthetics/tsconfig.json @@ -86,7 +86,10 @@ "@kbn/serverless", "@kbn/repo-info", "@kbn/index-management-plugin", - "@kbn/license-management-plugin" + "@kbn/license-management-plugin", + "@kbn/react-kibana-mount", + "@kbn/react-kibana-context-render", + "@kbn/react-kibana-context-theme" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/app/uptime_app.tsx b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/app/uptime_app.tsx index c82ac50a72594..1f4be8fa943f0 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/app/uptime_app.tsx +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/app/uptime_app.tsx @@ -7,11 +7,12 @@ import React, { useEffect } from 'react'; import { Provider as ReduxProvider } from 'react-redux'; import { Router } from '@kbn/shared-ux-router'; -import { EuiErrorBoundary } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { I18nStart, ChromeBreadcrumb, CoreStart, AppMountParameters } from '@kbn/core/public'; import { APP_WRAPPER_CLASS } from '@kbn/core/public'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { InspectorContextProvider } from '@kbn/observability-shared-plugin/public'; @@ -68,7 +69,6 @@ const Application = (props: UptimeAppProps) => { canSave, core, darkMode, - i18n: i18nCore, plugins, renderGlobalHelpControls, setBadge, @@ -99,66 +99,64 @@ const Application = (props: UptimeAppProps) => { store.dispatch(setBasePath(basePath)); return ( - - - - - - - - - - - - -
- - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
+ + + + + + + + + + + +
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
); }; diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ml/ml_flyout_container.tsx b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ml/ml_flyout_container.tsx index aeb779dca9883..88c571284306c 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ml/ml_flyout_container.tsx +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ml/ml_flyout_container.tsx @@ -7,9 +7,8 @@ import React, { useContext, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import type { Observable } from 'rxjs'; -import type { CoreTheme } from '@kbn/core/public'; -import { toMountPoint, useKibana } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import { canCreateMLJobSelector, hasMLJobSelector, @@ -43,7 +42,6 @@ const showMLJobNotification = ( range: { to: string; from: string }, success: boolean, awaitingNodeAssignment: boolean, - theme$?: Observable, error?: Error ) => { if (success) { @@ -51,7 +49,7 @@ const showMLJobNotification = ( { title: toMountPoint(

{labels.JOB_CREATED_SUCCESS_TITLE}

, - { theme$ } + kibanaService.core ), text: toMountPoint(

@@ -62,7 +60,7 @@ const showMLJobNotification = ( {labels.VIEW_JOB}

, - { theme$ } + kibanaService.core ), }, { toastLifeTimeMs: 10000 } @@ -116,8 +114,7 @@ export const MachineLearningFlyout: React.FC = ({ onClose }) => { basePath, { to: dateRangeEnd, from: dateRangeStart }, true, - hasMLJob.awaitingNodeAssignment, - core.services.theme?.theme$ + hasMLJob.awaitingNodeAssignment ); dispatch(getExistingMLJobAction.get({ monitorId: monitorId as string })); refreshApp(); @@ -134,7 +131,6 @@ export const MachineLearningFlyout: React.FC = ({ onClose }) => { { to: dateRangeEnd, from: dateRangeStart }, false, false, - core.services.theme?.theme$, error as Error ); } diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/lib/alert_types/alert_messages.tsx b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/lib/alert_types/alert_messages.tsx index 65bc0294fa03b..daa3f7a64ac39 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/lib/alert_types/alert_messages.tsx +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/lib/alert_types/alert_messages.tsx @@ -5,24 +5,18 @@ * 2.0. */ -import type { Observable } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import type { CoreTheme } from '@kbn/core/public'; import { EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import type { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import { ActionConnector } from '../../../../common/rules/types'; import { kibanaService } from '../../state/kibana_service'; import { getUrlForAlert } from './common'; -export const simpleAlertEnabled = ( - defaultActions: ActionConnector[], - theme$: Observable, - rule: Rule -) => { +export const simpleAlertEnabled = (defaultActions: ActionConnector[], rule: Rule) => { const alertUrl = getUrlForAlert(rule.id, kibanaService.core.http.basePath.get()); return { @@ -53,7 +47,7 @@ export const simpleAlertEnabled = ( })} , - { theme$ } + kibanaService.core ), }; }; diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/state/alerts/alerts.ts b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/state/alerts/alerts.ts index 4f6ced668dffb..3470c0c534d64 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/state/alerts/alerts.ts +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/state/alerts/alerts.ts @@ -153,7 +153,7 @@ export function* fetchAlertsEffect() { yield put(createAlertAction.success(response)); kibanaService.core.notifications.toasts.addSuccess( - simpleAlertEnabled(action.payload.defaultActions, kibanaService.theme, response) + simpleAlertEnabled(action.payload.defaultActions, response) ); yield put(getMonitorAlertsAction.get()); } catch (err) { diff --git a/x-pack/plugins/observability_solution/uptime/tsconfig.json b/x-pack/plugins/observability_solution/uptime/tsconfig.json index 1a8494ac19c11..8fc38a304c26b 100644 --- a/x-pack/plugins/observability_solution/uptime/tsconfig.json +++ b/x-pack/plugins/observability_solution/uptime/tsconfig.json @@ -73,7 +73,10 @@ "@kbn/core-saved-objects-server", "@kbn/observability-ai-assistant-plugin", "@kbn/shared-ux-link-redirect-app", - "@kbn/repo-info" + "@kbn/repo-info", + "@kbn/react-kibana-context-render", + "@kbn/react-kibana-context-theme", + "@kbn/react-kibana-mount" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/ux/public/application/ux_app.tsx b/x-pack/plugins/observability_solution/ux/public/application/ux_app.tsx index fa886ea1fbb3c..ff4c5620701bc 100644 --- a/x-pack/plugins/observability_solution/ux/public/application/ux_app.tsx +++ b/x-pack/plugins/observability_solution/ux/public/application/ux_app.tsx @@ -20,9 +20,10 @@ import { APP_WRAPPER_CLASS, } from '@kbn/core/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { KibanaContextProvider, - KibanaThemeProvider, useDarkMode, } from '@kbn/kibana-react-plugin/public'; @@ -129,51 +130,50 @@ export function UXAppRoot({ spaceId: string; }) { const { history } = appMountParameters; - const i18nCore = core.i18n; const plugins = { ...deps, maps }; createCallApmApi(core); return ( -
- - +
+ - - - + @@ -191,12 +191,12 @@ export function UXAppRoot({ - - - - - -
+ + +
+
+
+ ); } diff --git a/x-pack/plugins/observability_solution/ux/public/components/app/rum_dashboard/utils/test_helper.tsx b/x-pack/plugins/observability_solution/ux/public/components/app/rum_dashboard/utils/test_helper.tsx index 1cb31ba938626..7786e3eb92211 100644 --- a/x-pack/plugins/observability_solution/ux/public/components/app/rum_dashboard/utils/test_helper.tsx +++ b/x-pack/plugins/observability_solution/ux/public/components/app/rum_dashboard/utils/test_helper.tsx @@ -7,26 +7,20 @@ import React from 'react'; import { render as testLibRender } from '@testing-library/react'; -import { CoreStart } from '@kbn/core/public'; import { of } from 'rxjs'; import { createMemoryHistory } from 'history'; import { Router } from '@kbn/shared-ux-router'; import { MemoryHistory } from 'history'; -import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { coreMock } from '@kbn/core/public/mocks'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { UrlParamsProvider } from '../../../../context/url_params_context/url_params_context'; -export const core = { - http: { - basePath: { - prepend: jest.fn(), - }, - }, - uiSettings: { - get: (key: string) => true, - get$: (key: string) => of(true), - }, -} as unknown as CoreStart; +const core = coreMock.createStart(); +jest.spyOn(core.uiSettings, 'get').mockImplementation((_key: string) => true); +jest + .spyOn(core.uiSettings, 'get$') + .mockImplementation((_key: string) => of(true)); export const render = ( component: React.ReactNode, @@ -37,12 +31,12 @@ export const render = ( history.location.key = 'TestKeyForTesting'; return testLibRender( - - - - {component} - - - + + + + {component} + + + ); }; diff --git a/x-pack/plugins/observability_solution/ux/tsconfig.json b/x-pack/plugins/observability_solution/ux/tsconfig.json index aee39bbe0ea8f..d90159e8d30d6 100644 --- a/x-pack/plugins/observability_solution/ux/tsconfig.json +++ b/x-pack/plugins/observability_solution/ux/tsconfig.json @@ -45,7 +45,9 @@ "@kbn/shared-ux-link-redirect-app", "@kbn/apm-data-view", "@kbn/spaces-plugin", - "@kbn/deeplinks-observability" + "@kbn/deeplinks-observability", + "@kbn/react-kibana-context-render", + "@kbn/react-kibana-context-theme" ], "exclude": ["target/**/*"] } From 4795726f73b1f5d8f887968daaa9b9620f7657be Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 17 Apr 2024 18:22:38 +0100 Subject: [PATCH 17/36] skip flaky suite (#180091) --- .../public/assistant/get_comments/stream/use_stream.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.test.tsx b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.test.tsx index d65bb828287dc..7bc71c1d9c57f 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.test.tsx +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.test.tsx @@ -43,7 +43,9 @@ const defaultProps = { isError: false, actionTypeId: '.gen-ai', }; -describe('useStream', () => { + +// FLAKY: https://github.com/elastic/kibana/issues/180091 +describe.skip('useStream', () => { beforeEach(() => { jest.clearAllMocks(); }); From 7133c840cf7645aee6074260e6a92c0d2f7265cd Mon Sep 17 00:00:00 2001 From: Katerina Date: Wed, 17 Apr 2024 20:56:28 +0300 Subject: [PATCH 18/36] [Synthetics] Add URL validator on monitor creation form (#181004) ## Summary closes https://github.com/elastic/kibana/issues/177632 ### Before ![image](https://github.com/elastic/kibana/assets/3369346/94ff8b00-cd61-46b5-a787-fc313a263fc5) ### After https://github.com/elastic/kibana/assets/3369346/d487bef5-8f29-4102-98d9-d5482caf6324 --------- Co-authored-by: Carlos Crespo --- .../getting_started/simple_monitor_form.tsx | 9 +++++ .../utils/validators/is_url_valid.test.ts | 34 +++++++++++++++++++ .../utils/validators/is_url_valid.ts | 22 ++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/validators/is_url_valid.test.ts create mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/validators/is_url_valid.ts diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/simple_monitor_form.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/simple_monitor_form.tsx index 5aa77cf5f42c7..1233bb0390c7e 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/simple_monitor_form.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/simple_monitor_form.tsx @@ -22,6 +22,7 @@ import { ConfigKey, ServiceLocation, ServiceLocations } from '../../../../../com import { useCanEditSynthetics } from '../../../../hooks/use_capabilities'; import { useFormWrapped } from '../../../../hooks/use_form_wrapped'; import { NoPermissionsTooltip } from '../common/components/permissions'; +import { isUrlValid } from '../../utils/validators/is_url_valid'; export interface SimpleFormData { urls: string; @@ -75,6 +76,7 @@ export const SimpleMonitorForm = () => { {...register(ConfigKey.URLS, { validate: { notEmpty: (value: string) => (!Boolean(value.trim()) ? URL_REQUIRED_LABEL : true), + notValidUrl: (value: string) => (!isUrlValid(value) ? URL_INVALID_LABEL : true), }, })} isInvalid={!!urlError} @@ -156,3 +158,10 @@ export const URL_REQUIRED_LABEL = i18n.translate( defaultMessage: 'URL is required', } ); + +export const URL_INVALID_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.urlInvalidLabel', + { + defaultMessage: 'URL is not valid', + } +); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/validators/is_url_valid.test.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/validators/is_url_valid.test.ts new file mode 100644 index 0000000000000..8c1791524a5c5 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/validators/is_url_valid.test.ts @@ -0,0 +1,34 @@ +/* + * 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 { isUrlValid } from './is_url_valid'; + +describe('isUrlValid', () => { + it('should return false for undefined', () => { + expect(isUrlValid(undefined)).toBe(false); + }); + + it('should return false for null', () => { + expect(isUrlValid(null)).toBe(false); + }); + + it('should return false for non-URL strings', () => { + expect(isUrlValid('not a url')).toBe(false); + }); + it('should return false for non-URL empty strings', () => { + expect(isUrlValid('')).toBe(false); + }); + + it('should return false for URLs with disallowed schemes', () => { + expect(isUrlValid('ftp://example.com')).toBe(false); + }); + + it('should return true for URLs with allowed protocols', () => { + expect(isUrlValid('http://example.com')).toBe(true); + expect(isUrlValid('https://example.com')).toBe(true); + }); +}); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/validators/is_url_valid.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/validators/is_url_valid.ts new file mode 100644 index 0000000000000..07539d82544ca --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/validators/is_url_valid.ts @@ -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. + */ + +const allowedProtocols = ['http:', 'https:']; + +export const isUrlValid = (url?: string | null) => { + if (!url) return false; + + try { + const urlParsed = new URL(url); + + return ( + allowedProtocols.includes(urlParsed.protocol) && url.startsWith(`${urlParsed.protocol}//`) + ); + } catch (error) { + return false; + } +}; From d7ae4b6cf545093603e49e45eea54b33d36b6134 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 17 Apr 2024 19:33:05 +0100 Subject: [PATCH 19/36] chore(NA): remove prettier custom overrides across codebase (#180958) This PR is just deleting 4 custom overrides `.prettierrc` files and applying the auto fixes resulting from those changes. We've found some automation tasks were broken because we don't have a common code style across the codebase and we want to optimize for that with that change. Required for: https://github.com/elastic/kibana-operations/pull/93 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../scripts/steps/checks/prettier_topology.sh | 8 + .buildkite/scripts/steps/quick_checks.sh | 1 + scripts/prettier_topology_check.js | 10 + src/dev/run_prettier_topology_check.ts | 56 ++ x-pack/plugins/canvas/.prettierrc | 6 - .../observability_solution/apm/.prettierrc | 4 - .../configuration_types.d.ts | 4 +- .../agent_configuration_intake_rt.ts | 18 +- .../runtime_types/bytes_rt.ts | 19 +- .../runtime_types/duration_rt.test.ts | 26 +- .../runtime_types/duration_rt.ts | 22 +- .../runtime_types/float_rt.ts | 6 +- .../runtime_types/get_range_type_message.ts | 5 +- .../runtime_types/integer_rt.test.ts | 12 +- .../runtime_types/integer_rt.ts | 15 +- .../runtime_types/storage_size_rt.ts | 31 +- .../setting_definitions/general_settings.ts | 494 +++++++----------- .../setting_definitions/index.test.ts | 15 +- .../setting_definitions/index.ts | 4 +- .../setting_definitions/java_settings.ts | 297 +++++------ .../setting_definitions/mobile_settings.ts | 11 +- ...et_preferred_service_anomaly_timeseries.ts | 4 +- .../apm/common/anomaly_detection/index.ts | 18 +- .../apm/common/apm_api/parse_endpoint.ts | 5 +- .../apm/common/apm_feature_flags.ts | 6 +- .../apm/common/apm_telemetry.test.ts | 9 +- .../apm/common/apm_telemetry.ts | 9 +- .../apm/common/connections.ts | 4 +- .../apm/common/correlations/constants.ts | 6 +- .../get_prioritized_field_value_pairs.test.ts | 12 +- .../get_prioritized_field_value_pairs.ts | 4 +- .../utils/has_prefix_to_include.test.ts | 4 +- .../utils/has_prefix_to_include.ts | 4 +- ...get_aggregated_critical_path_root_nodes.ts | 37 +- .../critical_path/get_critical_path.test.ts | 16 +- .../common/critical_path/get_critical_path.ts | 9 +- .../common/custom_link/custom_link.test.ts | 7 +- .../apm/common/custom_link/index.ts | 19 +- .../apm/common/data_source.ts | 15 +- .../apm/common/dependencies.ts | 9 +- .../apm/common/environment_filter_values.ts | 15 +- .../apm/common/environment_rt.ts | 5 +- .../apm/common/es_fields/apm.ts | 47 +- .../apm/common/es_fields/es_fields.test.ts | 4 +- .../apm/common/fleet.ts | 15 +- .../observability_solution/apm/common/i18n.ts | 9 +- .../apm/common/latency_aggregation_types.ts | 3 +- .../apm/common/privilege_type.ts | 5 +- .../apm/common/rules/apm_rule_types.ts | 90 +--- .../common/rules/get_all_groupby_fields.ts | 12 +- .../apm/common/rules/schema.ts | 17 +- .../apm/common/serverless.test.ts | 8 +- .../apm/common/serverless.ts | 10 +- .../apm/common/service_groups.test.ts | 20 +- .../apm/common/service_groups.ts | 9 +- .../apm/common/service_health_status.ts | 16 +- .../apm/common/service_map.ts | 14 +- .../apm/common/service_nodes.ts | 9 +- .../apm/common/time_range_metadata.ts | 4 +- .../instructions/apm_agent_instructions.ts | 488 +++++++---------- .../instructions/apm_server_instructions.ts | 45 +- .../apm/common/tutorial/tutorials.ts | 3 +- .../common/utils/array_union_to_callable.ts | 4 +- .../apm/common/utils/environment_query.ts | 13 +- .../utils/esql/get_esql_date_range_filter.ts | 11 +- .../utils/esql/get_esql_environment_filter.ts | 5 +- .../apm/common/utils/esql/index.ts | 5 +- .../apm/common/utils/formatters/alert_url.ts | 13 +- .../common/utils/formatters/datetime.test.ts | 71 +-- .../apm/common/utils/formatters/datetime.ts | 16 +- .../common/utils/formatters/duration.test.ts | 52 +- .../apm/common/utils/formatters/duration.ts | 25 +- .../apm/common/utils/get_kuery_fields.test.ts | 33 +- .../get_kuery_with_mobile_filters.test.ts | 8 +- ...ferred_bucket_size_and_data_source.test.ts | 25 +- ...t_preferred_bucket_size_and_data_source.ts | 9 +- .../common/utils/join_by_key/index.test.ts | 8 +- .../apm/common/utils/join_by_key/index.ts | 3 +- .../apm/common/utils/kuery_utils.test.ts | 23 +- .../apm/common/utils/kuery_utils.ts | 7 +- .../cypress/e2e/alerts/error_count.cy.ts | 7 +- .../e2e/dependencies/dependencies.cy.ts | 20 +- .../generate_many_dependencies.ts | 13 +- .../cypress/e2e/diagnostics/diagnostics.cy.ts | 69 +-- .../cypress/e2e/errors/error_details.cy.ts | 4 +- .../cypress/e2e/errors/errors_page.cy.ts | 19 +- .../apm/ftr_e2e/cypress/e2e/home.cy.ts | 9 +- .../e2e/mobile/mobile_transactions.cy.ts | 12 +- .../cypress/e2e/onboarding/onboarding.cy.ts | 8 +- .../cypress/e2e/rules/error_count.cy.ts | 10 +- .../e2e/service_inventory/generate_data.ts | 8 +- .../header_filters/header_filters.cy.ts | 3 +- .../service_inventory/service_inventory.cy.ts | 42 +- .../cypress/e2e/service_map/service_map.cy.ts | 9 +- .../e2e/service_overview/alerts_table.cy.ts | 7 +- .../aws_lambda/aws_lambda.cy.ts | 3 +- .../azure_functions/azure_functions.cy.ts | 3 +- .../e2e/service_overview/header_filters.cy.ts | 55 +- .../service_overview/instances_table.cy.ts | 7 +- ...obile_overview_with_most_used_charts.cy.ts | 4 +- .../service_and_mobile_overview.cy.ts | 8 +- .../service_overview/service_overview.cy.ts | 77 +-- .../service_overview/time_comparison.cy.ts | 75 +-- .../e2e/settings/agent_configurations.cy.ts | 23 +- .../cypress/e2e/settings/custom_links.cy.ts | 13 +- .../storage_explorer/storage_explorer.cy.ts | 24 +- .../generate_span_links_data.ts | 59 +-- .../generate_span_stacktrace_data.ts | 10 +- .../e2e/transaction_details/span_links.cy.ts | 108 ++-- .../transaction_details.cy.ts | 110 ++-- .../transactions_overview.cy.ts | 21 +- .../cypress/fixtures/synthtrace/opbeans.ts | 9 +- .../apm/ftr_e2e/cypress/support/commands.ts | 78 ++- .../cypress/support/output_command_timings.ts | 6 +- .../apm/ftr_e2e/cypress/support/types.d.ts | 9 +- .../apm/ftr_e2e/cypress_test_runner.ts | 16 +- .../apm/ftr_e2e/ftr_config.ts | 3 +- .../apm/ftr_e2e/setup_cypress_node_events.ts | 11 +- .../apm/ftr_e2e/synthtrace.ts | 10 +- .../observability_solution/apm/jest.config.js | 4 +- .../apm/public/application/index.tsx | 6 +- .../get_apm_timeseries.tsx | 21 +- .../rule_types/anomaly_rule_type/index.tsx | 28 +- .../select_anomaly_detector.tsx | 18 +- .../select_anomaly_severity.test.tsx | 13 +- .../select_anomaly_severity.tsx | 9 +- .../error_count_rule_type/index.stories.tsx | 20 +- .../error_count_rule_type/index.tsx | 82 +-- .../rule_types/register_apm_rule_types.ts | 60 +-- .../index.stories.tsx | 16 +- .../transaction_duration_rule_type/index.tsx | 102 ++-- .../index.stories.tsx | 10 +- .../index.tsx | 90 ++-- .../failed_transaction_chart.tsx | 18 +- .../alert_details_app_section/helpers.ts | 4 +- .../alert_details_app_section/index.tsx | 15 +- .../latency_alerts_history_chart.tsx | 113 ++-- .../latency_chart.tsx | 58 +- .../throughput_chart.tsx | 79 ++- .../alert_details_app_section/types.ts | 9 +- .../ui_components/alerting_flyout/index.tsx | 14 +- .../ui_components/apm_rule_group_by.tsx | 5 +- .../ui_components/apm_rule_kql_filter.tsx | 28 +- .../apm_rule_params_container/index.tsx | 37 +- .../apm_rule_unified_search_bar.tsx | 8 +- .../chart_preview/chart_preview_helper.tsx | 4 +- .../ui_components/chart_preview/index.tsx | 17 +- .../ui_components/popover_expression.tsx | 8 +- .../components/alerting/utils/fields.test.tsx | 12 +- .../components/alerting/utils/fields.tsx | 76 +-- .../utils/get_alerting_capabilities.test.ts | 6 +- .../utils/get_alerting_capabilities.ts | 3 +- .../components/alerting/utils/helper.ts | 4 +- .../components/app/alerts_overview/index.tsx | 20 +- .../app/correlations/chart_title_tool_tip.tsx | 11 +- .../context_popover/field_stats_popover.tsx | 69 +-- .../app/correlations/correlations_table.tsx | 31 +- .../cross_cluster_search_warning.tsx | 6 +- .../failed_transactions_correlations.tsx | 121 ++--- ...transactions_correlations_help_popover.tsx | 9 +- ...get_transaction_distribution_chart_data.ts | 14 +- .../latency_correlations.test.tsx | 9 +- .../app/correlations/latency_correlations.tsx | 305 +++++------ .../app/correlations/progress_controls.tsx | 15 +- ..._failed_transactions_correlations.test.tsx | 101 ++-- .../use_failed_transactions_correlations.ts | 63 +-- .../app/correlations/use_fetch_params.ts | 19 +- .../use_latency_correlations.test.tsx | 53 +- .../correlations/use_latency_correlations.ts | 59 +-- ...nsactions_correlation_impact_label.test.ts | 28 +- .../utils/get_overall_histogram.ts | 16 +- .../dependencies_inventory_table/index.tsx | 23 +- .../index.tsx | 86 ++- .../dependencies_detail_table.tsx | 19 +- .../app/dependency_detail_overview/index.tsx | 7 +- .../app/dependency_detail_view/index.tsx | 6 +- ...ependency_operation_distribution_chart.tsx | 24 +- .../detail_view_header/index.tsx | 9 +- .../index.tsx | 35 +- ..._redirect_to_available_span_sample.test.ts | 7 +- ...maybe_redirect_to_available_span_sample.ts | 7 +- .../app/diagnostics/apm_documents_tab.tsx | 40 +- .../context/diagnostics_context.tsx | 14 +- .../app/diagnostics/data_stream_tab.tsx | 28 +- .../app/diagnostics/import_export_tab.tsx | 16 +- .../components/app/diagnostics/index.tsx | 33 +- .../index_pattern_settings_tab.tsx | 93 ++-- .../app/diagnostics/index_templates_tab.tsx | 27 +- .../app/diagnostics/indices_tab.tsx | 4 +- .../apm_integration_package_status.tsx | 6 +- .../summary_tab/data_streams_status.tsx | 6 +- .../app/diagnostics/summary_tab/index.tsx | 29 +- .../summary_tab/index_templates_status.tsx | 24 +- .../summary_tab/indicies_status.tsx | 6 +- .../diagnostics/summary_tab/tab_status.tsx | 10 +- .../distribution/index.tsx | 12 +- .../error_sample_contextual_insight.tsx | 28 +- .../error_sampler/error_sample_detail.tsx | 85 +-- .../error_sampler/error_tabs.tsx | 6 +- .../exception_stacktrace.stories.tsx | 122 ++--- .../exception_stacktrace.test.tsx | 11 +- .../error_sampler/exception_stacktrace.tsx | 17 +- .../error_sampler/index.tsx | 12 +- .../error_sampler/plaintext_stacktrace.tsx | 6 +- .../error_sampler/sample_summary.tsx | 4 +- .../app/error_group_details/index.tsx | 80 +-- .../top_erroneous_transactions/index.tsx | 68 +-- .../error_group_list/index.tsx | 101 ++-- .../use_error_group_list_data.tsx | 92 ++-- .../app/error_group_overview/index.tsx | 28 +- .../infra_tabs/empty_prompt.tsx | 28 +- .../app/infra_overview/infra_tabs/index.tsx | 23 +- .../infra_overview/infra_tabs/use_tabs.tsx | 6 +- .../public/components/app/metrics/index.tsx | 5 +- .../metrics/jvm_metrics_overview/index.tsx | 61 +-- .../serverless_active_instances.tsx | 53 +- .../serverless_function_name_link.tsx | 5 +- .../serverless_functions.tsx | 54 +- .../serverless_metrics_charts.tsx | 54 +- .../serverless_metrics/serverless_summary.tsx | 79 ++- .../app/metrics/service_metrics/index.tsx | 7 +- .../app/metrics/static_dashboard/helper.ts | 7 +- .../app/metrics/static_dashboard/index.tsx | 56 +- .../service_node_metrics/index.tsx | 39 +- .../app/mobile/charts/http_requests_chart.tsx | 37 +- .../index.tsx | 45 +- .../treemap_select.tsx | 54 +- .../charts/mobile_http_error_rate/index.tsx | 46 +- .../charts/mobile_most_affected/index.tsx | 36 +- .../mobile_most_affected/treemap_select.tsx | 41 +- .../mobile/charts/mobile_treemap/index.tsx | 41 +- .../charts/mobile_treemap/treemap_select.tsx | 43 +- .../app/mobile/charts/sessions_chart.tsx | 26 +- .../crash_group_details/index.tsx | 86 ++- .../error_group_details/index.tsx | 111 ++-- .../shared/distribution/index.tsx | 12 +- .../crash_group_list/index.tsx | 81 +-- .../crashes_overview.tsx | 39 +- .../error_group_list/index.tsx | 73 +-- .../errors_overview.tsx | 55 +- .../components/app/mobile/search_bar.tsx | 13 +- .../mobile/service_overview/filters/index.tsx | 40 +- .../geo_map/embedded_map.test.tsx | 16 +- .../service_overview/geo_map/embedded_map.tsx | 57 +- .../geo_map/embedded_map_select.tsx | 29 +- .../get_http_requests_map_layer_list.ts | 40 +- .../geo_map/map_layers/get_map_layer_style.ts | 5 +- .../map_layers/get_session_map_layer_list.ts | 35 +- .../app/mobile/service_overview/index.tsx | 38 +- .../most_used_charts/index.tsx | 49 +- .../most_used_charts/sunburst_chart.tsx | 33 +- .../service_overview/stats/location_stats.tsx | 73 +-- .../mobile/service_overview/stats/stats.tsx | 72 +-- .../app_version_tab.tsx | 33 +- .../transaction_overview_tabs/devices_tab.tsx | 24 +- .../os_version_tab.tsx | 33 +- .../stats_list/get_columns.tsx | 51 +- .../stats_list/index.tsx | 7 +- .../transactions_tab.tsx | 9 +- .../use_mobile_statistics_fetcher.ts | 90 ++-- .../app/onboarding/agent_config_table.tsx | 12 +- .../onboarding/agent_status_instructions.tsx | 25 +- .../app/onboarding/commands/django.ts | 9 +- .../commands/get_apm_agent_commands.ts | 62 +-- .../app/onboarding/commands/node.ts | 3 +- .../components/app/onboarding/footer.tsx | 14 +- .../components/app/onboarding/index.tsx | 33 +- .../instructions/api_key_callout.tsx | 30 +- .../onboarding/instructions/django_agent.tsx | 15 +- .../onboarding/instructions/dotnet_agent.tsx | 83 +-- .../onboarding/instructions/flask_agent.tsx | 15 +- .../app/onboarding/instructions/go_agent.tsx | 9 +- .../onboarding/instructions/java_agent.tsx | 48 +- .../onboarding/instructions/node_agent.tsx | 9 +- .../onboarding/instructions/otel_agent.tsx | 87 ++- .../app/onboarding/instructions/php_agent.tsx | 57 +- .../onboarding/instructions/rack_agent.tsx | 9 +- .../onboarding/instructions/rails_agent.tsx | 9 +- .../app/onboarding/instructions_set.tsx | 19 +- .../app/onboarding/introduction.tsx | 33 +- .../app/onboarding/serverless_instructions.ts | 6 +- .../host_names_filter_warning.tsx | 11 +- .../app/profiling_overview/index.tsx | 6 +- .../profiling_hosts_callout.tsx | 19 +- .../profiling_hosts_flamegraph.tsx | 37 +- .../profiling_hosts_top_functions.tsx | 52 +- .../actions/goto_dashboard.tsx | 6 +- .../actions/link_dashboard.tsx | 5 +- .../actions/save_dashboard_modal.tsx | 118 ++--- .../actions/unlink_dashboard.tsx | 33 +- .../app/service_dashboards/context_menu.tsx | 14 +- .../service_dashboards/dashboard_selector.tsx | 24 +- .../service_dashboards/empty_dashboards.tsx | 24 +- .../app/service_dashboards/index.tsx | 77 +-- .../app/service_dependencies/index.tsx | 9 +- .../service_dependencies_breakdown_chart.tsx | 33 +- .../refresh_service_groups_subscriber.tsx | 4 +- .../service_group_save/edit_button.tsx | 11 +- .../service_group_save/group_details.tsx | 76 ++- .../service_group_save/save_button.tsx | 6 +- .../service_group_save/save_modal.tsx | 64 +-- .../service_group_save/select_services.tsx | 120 ++--- .../service_list_preview.tsx | 43 +- .../service_groups_button_group.tsx | 23 +- .../service_groups_list/index.tsx | 97 ++-- .../service_group_card.tsx | 51 +- .../service_groups_list.tsx | 6 +- .../service_groups_list/service_stat.tsx | 12 +- .../service_groups/service_groups_tour.tsx | 13 +- .../use_service_groups_tour.tsx | 5 +- .../app/service_inventory/index.tsx | 41 +- .../service_inventory.stories.tsx | 4 +- .../column_header_with_tooltip.tsx | 5 +- .../service_list/health_badge.tsx | 6 +- .../service_inventory/service_list/index.tsx | 141 ++--- .../service_list/order_service_items.ts | 19 +- .../service_list/service_list.stories.tsx | 7 +- .../service_list/service_list.test.tsx | 16 +- .../components/app/service_logs/index.tsx | 33 +- .../__stories__/cytoscape.stories.tsx | 6 +- .../cytoscape_example_data.stories.tsx | 25 +- .../generate_service_map_elements.ts | 11 +- .../app/service_map/controls.test.tsx | 8 +- .../components/app/service_map/controls.tsx | 16 +- .../components/app/service_map/cytoscape.tsx | 20 +- .../app/service_map/cytoscape_options.ts | 40 +- .../app/service_map/empty_banner.test.tsx | 8 +- .../app/service_map/empty_banner.tsx | 8 +- .../components/app/service_map/icons.ts | 10 +- .../components/app/service_map/index.tsx | 56 +- .../service_map/popover/anomaly_detection.tsx | 31 +- .../popover/dependency_contents.tsx | 15 +- .../popover/externals_list_contents.tsx | 37 +- .../app/service_map/popover/index.tsx | 39 +- .../app/service_map/popover/popover.test.tsx | 15 +- .../service_map/popover/resource_contents.tsx | 13 +- .../service_map/popover/service_contents.tsx | 54 +- .../app/service_map/popover/stats_list.tsx | 96 +--- .../app/service_map/timeout_prompt.tsx | 6 +- .../use_cytoscape_event_handlers.test.tsx | 8 +- .../use_cytoscape_event_handlers.ts | 15 +- .../get_throughput_screen_context.ts | 6 +- .../components/app/service_overview/index.tsx | 50 +- .../service_overview.stories.tsx | 14 +- .../service_overview.test.tsx | 4 +- .../index.tsx | 64 +-- .../service_overview_errors_table/index.tsx | 6 +- ...ice_overview_instances_chart_and_table.tsx | 44 +- .../get_columns.tsx | 107 ++-- .../index.tsx | 42 +- .../instance_actions_menu/index.tsx | 16 +- .../instance_actions_menu/menu_sections.ts | 45 +- .../instance_details.test.tsx | 66 +-- .../intance_details.tsx | 45 +- .../service_overview_throughput_chart.tsx | 80 +-- .../index.stories.tsx | 14 +- .../agent_configuration_create_edit/index.tsx | 10 +- .../service_page/form_row_select.tsx | 17 +- .../form_row_suggestions_select.tsx | 13 +- .../service_page/service_page.tsx | 122 ++--- .../settings_page/save_config.ts | 7 +- .../settings_page/setting_form_row.tsx | 12 +- .../settings_page/settings_page.tsx | 43 +- .../settings/agent_configurations/index.tsx | 19 +- .../list/confirm_delete_modal.tsx | 91 ++-- .../agent_configurations/list/index.tsx | 92 ++-- .../agent_explorer_docs_link/index.tsx | 14 +- .../agent_contextual_information/index.tsx | 52 +- .../agent_instances_details/index.tsx | 48 +- .../agent_explorer/agent_instances/index.tsx | 36 +- .../agent_latest_version/index.tsx | 19 +- .../agent_explorer/agent_list/index.tsx | 91 +--- .../app/settings/agent_explorer/index.tsx | 63 +-- .../settings/agent_keys/agent_keys_table.tsx | 78 +-- .../agent_keys/confirm_delete_modal.tsx | 29 +- .../settings/agent_keys/create_agent_key.tsx | 65 +-- .../create_agent_key/agent_key_callout.tsx | 29 +- .../app/settings/agent_keys/index.tsx | 74 +-- .../prompts/api_keys_not_enabled.tsx | 18 +- .../agent_keys/prompts/permission_denied.tsx | 18 +- .../anomaly_detection/add_environments.tsx | 54 +- .../settings/anomaly_detection/create_jobs.ts | 27 +- .../app/settings/anomaly_detection/index.tsx | 9 +- .../settings/anomaly_detection/jobs_list.tsx | 109 ++-- .../anomaly_detection/jobs_list_status.tsx | 48 +- .../app/settings/apm_indices/index.tsx | 124 ++--- .../delete_button.test.tsx | 7 +- .../delete_button.tsx | 5 +- .../documentation.tsx | 5 +- .../filters_section.tsx | 72 +-- .../flyout_footer.tsx | 4 +- .../helper.test.ts | 8 +- .../create_edit_custom_link_flyout/helper.ts | 71 +-- .../create_edit_custom_link_flyout/index.tsx | 20 +- .../link_preview.stories.tsx | 9 +- .../link_preview.test.tsx | 34 +- .../link_preview.tsx | 34 +- .../link_section.tsx | 67 +-- .../save_custom_link.ts | 21 +- .../custom_link/custom_link_table.tsx | 31 +- .../app/settings/custom_link/empty_prompt.tsx | 6 +- .../app/settings/custom_link/index.test.tsx | 12 +- .../app/settings/custom_link/index.tsx | 20 +- .../app/settings/general_settings/index.tsx | 24 +- .../settings/schema/confirm_switch_modal.tsx | 75 +-- .../components/app/settings/schema/index.tsx | 62 +-- .../schema/migrated/card_footer_content.tsx | 7 +- .../migrated/successful_migration_card.tsx | 11 +- .../migrated/upgrade_available_card.tsx | 12 +- .../schema/migration_in_progress_panel.tsx | 7 +- .../app/settings/schema/schema.stories.tsx | 16 +- .../app/settings/schema/schema_overview.tsx | 59 +-- .../get_storage_explorer_links.ts | 4 +- .../components/app/storage_explorer/index.tsx | 140 ++--- .../index_lifecycle_phase_select.tsx | 76 +-- .../prompts/permission_denied.tsx | 18 +- .../resources/tips_and_resources.tsx | 109 ++-- .../storage_explorer/services_table/index.tsx | 121 ++--- .../index_stats_per_service.tsx | 72 +-- .../services_table/size_label.tsx | 16 +- .../storage_details_per_service.tsx | 180 +++---- .../app/storage_explorer/storage_chart.tsx | 5 +- .../app/storage_explorer/summary_stats.tsx | 86 +-- .../app/top_traces_overview/trace_list.tsx | 53 +- .../components/app/trace_explorer/index.tsx | 13 +- .../trace_explorer_waterfall.tsx | 5 +- .../trace_explorer/trace_search_box/index.tsx | 29 +- .../components/app/trace_link/index.tsx | 23 +- .../components/app/trace_overview/index.tsx | 5 +- .../aggregated_critical_path_tab.tsx | 25 +- .../distribution/index.test.tsx | 5 +- .../distribution/index.tsx | 12 +- ...use_transaction_distribution_chart_data.ts | 97 ++-- .../failed_transactions_correlations_tab.tsx | 23 +- .../app/transaction_details/index.tsx | 8 +- .../latency_correlations_tab.tsx | 9 +- .../app/transaction_details/profiling_tab.tsx | 41 +- .../transaction_details/top_errors/index.tsx | 46 +- .../transaction_details_tabs.tsx | 26 +- .../use_waterfall_fetcher.ts | 5 +- .../waterfall_with_summary/index.tsx | 18 +- .../maybe_view_trace_link.tsx | 26 +- .../percent_of_parent.tsx | 32 +- .../transaction_tabs.tsx | 16 +- .../waterfall_container/index.tsx | 17 +- .../waterfall/accordion_waterfall.tsx | 29 +- .../waterfall/badge/span_links_badge.tsx | 44 +- .../waterfall/badge/sync_badge.tsx | 18 +- .../waterfall/flyout_top_level_properties.tsx | 5 +- .../waterfall_container/waterfall/index.tsx | 22 +- .../waterfall/orphan_trace_items_warning.tsx | 28 +- .../waterfall/span_flyout/index.tsx | 42 +- .../waterfall/span_flyout/span_db.tsx | 16 +- .../span_flyout/span_flyout.stories.tsx | 16 +- .../span_flyout/sticky_span_properties.tsx | 32 +- .../span_flyout/truncate_height_section.tsx | 3 +- .../dropped_spans_warning.tsx | 6 +- .../waterfall/transaction_flyout/index.tsx | 12 +- .../transaction_flyout.stories.tsx | 4 +- .../waterfall/waterfall_flyout.tsx | 14 +- .../waterfall_helpers.test.ts | 24 +- .../waterfall_helpers/waterfall_helpers.ts | 117 ++--- .../waterfall/waterfall_item.tsx | 54 +- .../waterfall_container.stories.tsx | 4 +- .../waterfall_container/waterfall_legends.tsx | 9 +- .../app/transaction_details_link/index.tsx | 21 +- .../components/app/transaction_link/index.tsx | 5 +- .../app/transaction_overview/index.tsx | 11 +- .../transaction_overview.test.tsx | 31 +- .../agent_instructions_accordion.tsx | 43 +- .../fleet_integration/apm_agents/index.tsx | 8 +- .../default_discovery_rule.tsx | 8 +- .../edit_discovery_rule.tsx | 16 +- .../apm_agents/runtime_attachment/index.tsx | 48 +- .../java_agent_version_input.tsx | 11 +- .../runtime_attachment.stories.tsx | 12 +- .../runtime_attachment/runtime_attachment.tsx | 7 +- .../java_runtime_attachment.tsx | 42 +- .../apm_custom_assets_extension.tsx | 12 +- .../apm_enrollment_flyout_extension.tsx | 16 +- .../apm_policy_form/index.tsx | 73 +-- .../agent_authorization_settings.test.ts | 36 +- .../agent_authorization_settings.ts | 12 +- .../settings_definition/apm_settings.test.ts | 22 +- .../settings_definition/apm_settings.ts | 106 ++-- .../settings_definition/debug_settings.ts | 14 +- .../settings_definition/rum_settings.ts | 69 +-- .../tail_sampling_settings.test.tsx | 5 +- .../tail_sampling_settings.tsx | 3 +- .../settings_definition/tls_settings.test.ts | 10 +- .../settings_definition/tls_settings.ts | 57 +- .../settings_form/form_row_setting.tsx | 27 +- .../apm_policy_form/settings_form/index.tsx | 32 +- .../settings_form/utils.test.ts | 6 +- .../apm_policy_form/settings_form/utils.ts | 31 +- .../lazy_apm_custom_assets_extension.tsx | 4 +- .../lazy_apm_policy_create_extension.tsx | 4 +- .../lazy_apm_policy_edit_extension.tsx | 4 +- .../components/routing/apm_error_boundary.tsx | 5 +- .../components/routing/apm_route_config.tsx | 7 +- .../alerting_popover_flyout.tsx | 34 +- .../anomaly_detection_setup_link.test.tsx | 18 +- .../anomaly_detection_setup_link.tsx | 55 +- .../app_root/apm_header_action_menu/index.tsx | 18 +- .../inspector_header_link.tsx | 6 +- .../labs/labs_flyout.tsx | 32 +- .../components/routing/app_root/index.tsx | 28 +- .../index.tsx | 6 +- .../index.test.tsx | 31 +- .../index.tsx | 6 +- .../redirect_with_offset/index.test.tsx | 37 +- .../app_root/redirect_with_offset/index.tsx | 17 +- .../public/components/routing/home/index.tsx | 32 +- .../routing/home/legacy_backends.tsx | 5 +- .../routing/home/storage_explorer.tsx | 9 +- .../routing/mobile_service_detail/index.tsx | 34 +- .../routing/service_detail/index.tsx | 21 +- ...redirect_to_default_service_route_view.tsx | 4 +- .../edit_agent_configuration_route_view.tsx | 22 +- .../components/routing/settings/index.tsx | 46 +- .../routing/templates/apm_main_template.tsx | 35 +- .../analyze_data_button.tsx | 10 +- .../templates/apm_service_template/index.tsx | 77 +-- .../templates/dependency_detail_template.tsx | 14 +- .../mobile_service_template/index.tsx | 62 +-- .../routing/templates/no_data_config.ts | 27 +- .../templates/service_group_template.tsx | 13 +- .../routing/templates/settings_template.tsx | 13 +- .../aggregated_transactions_badge/index.tsx | 9 +- .../shared/charts/breakdown_chart/index.tsx | 39 +- .../shared/charts/chart_container.test.tsx | 32 +- .../shared/charts/chart_container.tsx | 11 +- .../index.test.tsx | 4 +- .../duration_distribution_chart/index.tsx | 34 +- .../total_doc_count_label.tsx | 34 +- .../index.test.ts | 4 +- .../index.tsx | 55 +- .../failed_transaction_rate_chart/index.tsx | 19 +- .../shared/charts/flamegraph/index.tsx | 9 +- .../helper/get_chart_anomaly_timeseries.tsx | 9 +- .../shared/charts/helper/get_empty_series.ts | 5 +- .../helper/get_timezone_offset_in_ms.test.ts | 3 +- .../shared/charts/helper/helper.test.ts | 6 +- .../components/shared/charts/helper/helper.ts | 8 +- .../shared/charts/helper/timezone.test.ts | 20 +- .../shared/charts/helper/timezone.ts | 8 +- .../custom_tooltip.stories.tsx | 26 +- .../custom_tooltip.tsx | 74 +-- .../index.tsx | 22 +- ...ces_latency_distribution_chart.stories.tsx | 42 +- .../shared/charts/latency_chart/index.tsx | 46 +- .../latency_chart/latency_chart.stories.tsx | 5 +- .../shared/charts/metrics_chart/index.tsx | 11 +- .../shared/charts/spark_plot/index.tsx | 11 +- .../shared/charts/timeline/index.tsx | 21 +- .../timeline/marker/error_marker.test.tsx | 13 +- .../charts/timeline/marker/error_marker.tsx | 12 +- .../shared/charts/timeline/marker/index.tsx | 6 +- .../shared/charts/timeline/timeline_axis.tsx | 27 +- .../shared/charts/timeline/vertical_lines.tsx | 13 +- .../shared/charts/timeseries_chart.tsx | 44 +- .../charts/timeseries_chart_with_context.tsx | 6 +- .../transaction_breakdown_chart/index.tsx | 19 +- .../use_transaction_breakdown.ts | 35 +- .../charts/transaction_charts/helper.test.ts | 15 +- .../charts/transaction_charts/index.tsx | 19 +- .../charts/transaction_charts/ml_header.tsx | 25 +- .../index.tsx | 16 +- .../shared/charts/treemap_chart/index.tsx | 10 +- .../critical_path_flamegraph_tooltip.tsx | 17 +- .../critical_path_to_flamegraph.ts | 26 +- .../shared/critical_path_flamegraph/index.tsx | 62 +-- .../shared/date_picker/apm_date_picker.tsx | 8 +- .../shared/date_picker/date_picker.test.tsx | 11 +- .../components/shared/date_picker/index.tsx | 17 +- .../dependencies_table_service_map_link.tsx | 5 +- .../get_span_metric_columns.tsx | 55 +- .../shared/dependencies_table/index.tsx | 20 +- ...pendency_failed_transaction_rate_chart.tsx | 10 +- .../dependency_latency_chart.tsx | 19 +- .../dependency_throughput_chart.tsx | 21 +- .../shared/dependency_metric_charts/index.tsx | 29 +- .../shared/environment_badge/index.tsx | 12 +- .../shared/environment_filter/index.tsx | 4 +- .../shared/environment_select/index.tsx | 8 +- .../shared/errors_table/get_columns.tsx | 31 +- .../shared/height_retainer/index.tsx | 13 +- .../resetting_height_container.tsx | 15 +- .../shared/impact_bar/impact_bar.test.js | 4 +- .../components/shared/impact_bar/index.tsx | 11 +- .../shared/key_value_filter_list/index.tsx | 24 +- .../key_value_filter_list.test.tsx | 19 +- .../key_value_table/formatted_value.tsx | 8 +- .../shared/key_value_table/index.tsx | 12 +- .../components/shared/kuery_bar/index.tsx | 3 +- .../shared/kuery_bar/typeahead/index.js | 8 +- .../shared/kuery_bar/typeahead/suggestion.js | 7 +- .../shared/kuery_bar/typeahead/suggestions.js | 10 +- .../shared/license_prompt/index.tsx | 4 +- .../shared/links/apm/apm_link.test.tsx | 18 +- .../components/shared/links/apm/apm_link.tsx | 11 +- .../shared/links/apm/error_detail_link.tsx | 7 +- .../shared/links/apm/error_overview_link.tsx | 8 +- .../links/apm/mobile/crash_detail_link.tsx | 8 +- .../links/apm/mobile/error_detail_link.tsx | 8 +- .../links/apm/mobile/error_overview_link.tsx | 23 +- .../shared/links/apm/service_link/index.tsx | 11 +- .../apm/service_link/service_link.test.tsx | 4 +- .../shared/links/apm/service_map_link.tsx | 8 +- .../apm/service_node_metric_overview_link.tsx | 18 +- ...ervice_transactions_overview_link.test.tsx | 15 +- .../service_transactions_overview_link.tsx | 6 +- .../apm/transaction_detail_link/index.tsx | 13 +- .../apm/transaction_overview_link.test.tsx | 28 +- .../links/apm/transaction_overview_link.tsx | 8 +- .../discover_links/discover_error_link.tsx | 9 +- .../links/discover_links/discover_link.tsx | 4 +- .../discover_links.integration.test.tsx | 31 +- .../discover_transaction_button.test.tsx | 9 +- .../discover_transaction_link.tsx | 10 +- .../shared/links/infra_link.test.tsx | 8 +- .../mlexplorer_link.test.tsx | 11 +- .../mlmanage_jobs_link.test.tsx | 3 +- .../mlsingle_metric_link.test.tsx | 10 +- .../components/shared/links/rison_helpers.ts | 8 +- .../shared/links/setup_instructions_link.tsx | 15 +- .../shared/links/url_helpers.test.tsx | 10 +- .../components/shared/links/url_helpers.ts | 15 +- .../components/shared/managed_table/index.tsx | 34 +- .../metadata_table/error_metadata/index.tsx | 32 +- .../shared/metadata_table/helper.ts | 39 +- .../shared/metadata_table/index.tsx | 11 +- .../metadata_table/metadata_table.test.tsx | 5 +- .../shared/metadata_table/section.tsx | 7 +- .../metadata_table/span_metadata/index.tsx | 32 +- .../transaction_metadata/index.tsx | 32 +- .../components/shared/ml_callout/index.tsx | 46 +- .../shared/overview_table_container/index.tsx | 3 +- .../profiling/flamegraph/flamegraph_link.tsx | 7 +- .../shared/profiling/flamegraph/index.tsx | 37 +- .../shared/profiling/top_functions/index.tsx | 41 +- .../top_functions/top_functions_link.tsx | 7 +- .../shared/search_bar/search_bar.test.tsx | 14 +- .../shared/search_bar/search_bar.tsx | 13 +- .../shared/select_with_placeholder/index.tsx | 5 +- .../shared/service_icons/cloud_details.tsx | 71 +-- .../service_icons/container_details.tsx | 43 +- .../shared/service_icons/icon_popover.tsx | 13 +- .../shared/service_icons/index.test.tsx | 39 +- .../components/shared/service_icons/index.tsx | 63 +-- .../shared/service_icons/otel_details.tsx | 31 +- .../service_icons/serverless_details.tsx | 43 +- .../shared/service_icons/service_details.tsx | 36 +- .../service_icons/service_icons.stories.tsx | 16 +- .../components/shared/slo_callout/index.tsx | 11 +- .../shared/span_icon/span_icon.stories.tsx | 25 +- .../components/shared/span_links/index.tsx | 39 +- .../span_links/span_links_tab_content.tsx | 7 +- .../shared/span_links/span_links_table.tsx | 52 +- .../stacktrace/cause_stacktrace.test.tsx | 8 +- .../shared/stacktrace/cause_stacktrace.tsx | 9 +- .../components/shared/stacktrace/context.tsx | 4 +- .../shared/stacktrace/frame_heading.test.tsx | 23 +- .../shared/stacktrace/frame_heading.tsx | 17 +- .../c_sharp_frame_heading_renderer.tsx | 7 +- .../default_frame_heading_renderer.tsx | 6 +- .../components/shared/stacktrace/index.tsx | 9 +- .../stacktrace/library_stacktrace.test.tsx | 12 +- .../shared/stacktrace/library_stacktrace.tsx | 12 +- .../shared/stacktrace/stackframe.test.tsx | 12 +- .../shared/stacktrace/stackframe.tsx | 8 +- .../shared/stacktrace/stacktrace.test.ts | 4 +- .../shared/stacktrace/variables.tsx | 7 +- .../shared/sticky_properties/index.tsx | 11 +- .../sticky_properties.test.tsx | 12 +- .../shared/suggestions_select/index.tsx | 8 +- .../suggestions_select.stories.tsx | 7 +- .../shared/summary/duration_summary_item.tsx | 3 +- .../error_count_summary_item_badge.test.tsx | 5 +- .../error_count_summary_item_badge.tsx | 3 +- .../summary/http_info_summary_item/index.tsx | 9 +- .../http_status_badge.test.tsx | 24 +- .../summary/transaction_summary.test.tsx | 8 +- .../shared/summary/transaction_summary.tsx | 11 +- .../summary/user_agent_summary_item.test.tsx | 4 +- .../summary/user_agent_summary_item.tsx | 14 +- .../table_search_bar/table_search_bar.tsx | 6 +- .../shared/technical_preview_badge.tsx | 11 +- .../shared/time_comparison/comparison.test.ts | 4 +- .../time_comparison/get_comparison_enabled.ts | 4 +- .../time_comparison/get_comparison_options.ts | 23 +- .../shared/time_comparison/index.test.tsx | 95 ++-- .../shared/time_comparison/index.tsx | 34 +- .../shared/timestamp_tooltip/index.test.tsx | 3 +- .../shared/timestamp_tooltip/index.tsx | 5 +- .../__fixtures__/mock_data.ts | 3 +- .../custom_link_list.test.tsx | 9 +- .../custom_link_list.tsx | 14 +- .../custom_link_toolbar.test.tsx | 13 +- .../custom_link_toolbar.tsx | 14 +- .../custom_link_menu_section/index.test.tsx | 35 +- .../custom_link_menu_section/index.tsx | 43 +- .../transaction_action_menu/sections.test.ts | 11 +- .../transaction_action_menu/sections.ts | 140 ++--- .../transaction_action_menu.test.tsx | 128 ++--- .../transaction_action_menu.tsx | 33 +- .../shared/transactions_table/get_columns.tsx | 126 ++--- .../get_latency_column_label.ts | 13 +- .../shared/transactions_table/index.tsx | 279 ++++------ .../shared/truncate_with_tooltip/index.tsx | 6 +- .../components/shared/try_it_button/index.tsx | 20 +- .../shared/unified_search_bar/index.tsx | 62 +-- .../unified_search_bar.test.tsx | 20 +- .../annotations/annotations_context.tsx | 25 +- .../anomaly_detection_jobs_context.tsx | 13 +- .../apm_plugin/mock_apm_plugin_storybook.tsx | 5 +- .../apm_service/apm_service_context.tsx | 30 +- .../use_service_transaction_types_fetcher.tsx | 15 +- .../public/context/breadcrumbs/context.tsx | 22 +- .../context/breadcrumbs/use_breadcrumb.ts | 5 +- .../chart_pointer_event_context.tsx | 6 +- .../use_chart_pointer_event_context.tsx | 15 +- .../environments_context.tsx | 3 +- .../use_kibana_environment_context.tsx | 5 +- .../license/invalid_license_notification.tsx | 4 +- .../context/license/license_context.tsx | 4 +- .../service_anomaly_timeseries_context.tsx | 48 +- .../time_range_id/time_range_id_context.tsx | 12 +- .../time_range_metadata_context.tsx | 31 +- .../use_search_service_destination_metrics.ts | 3 +- .../url_params_context/helpers.test.ts | 5 +- .../url_params_context/resolve_url_params.ts | 12 +- .../url_params_context.test.tsx | 10 +- .../url_params_context/url_params_context.tsx | 76 ++- .../hooks/create_shared_use_fetcher.tsx | 21 +- .../public/hooks/use_adhoc_apm_data_view.ts | 6 +- .../apm/public/hooks/use_apm_feature_flag.ts | 9 +- .../apm/public/hooks/use_apm_router.ts | 3 +- .../apm/public/hooks/use_breakpoints.ts | 5 +- .../use_crash_group_distribution_fetcher.tsx | 41 +- ...e_critical_path_feature_enabled_setting.ts | 3 +- .../public/hooks/use_date_range_redirect.ts | 3 +- .../apm/public/hooks/use_debounce.tsx | 5 +- ...lt_ai_assistant_starter_prompts_for_apm.ts | 11 +- .../public/hooks/use_default_environment.ts | 3 +- .../public/hooks/use_default_time_range.ts | 7 +- ...dependency_detail_operations_breadcrumb.ts | 7 +- .../use_error_group_distribution_fetcher.tsx | 41 +- .../use_fallback_to_transactions_fetcher.tsx | 3 +- .../hooks/use_fetcher.integration.test.tsx | 17 +- .../apm/public/hooks/use_fetcher.test.tsx | 7 +- .../apm/public/hooks/use_fetcher.tsx | 38 +- .../use_filters_for_embeddable_charts.ts | 5 +- ...e_preferred_data_source_and_bucket_size.ts | 27 +- ...se_preferred_service_anomaly_timeseries.ts | 4 +- .../public/hooks/use_previous_period_text.ts | 5 +- .../use_profiling_integration_setting.ts | 9 +- .../apm/public/hooks/use_profiling_plugin.tsx | 3 +- .../public/hooks/use_progressive_fetcher.tsx | 49 +- .../use_service_metric_charts_fetcher.ts | 27 +- .../apm/public/hooks/use_time_range.test.ts | 5 +- .../apm/public/hooks/use_time_range.ts | 5 +- .../hooks/use_trace_explorer_samples.ts | 4 +- .../use_transaction_latency_chart_fetcher.ts | 62 +-- .../use_transaction_trace_samples_fetcher.ts | 48 +- .../locator/service_detail_locator.test.ts | 5 +- .../public/locator/service_detail_locator.ts | 8 +- .../apm/public/plugin.ts | 104 ++-- .../selectors/latency_chart_selector.test.ts | 5 +- .../selectors/latency_chart_selectors.ts | 26 +- .../apm/public/services/call_api.test.ts | 9 +- ...pm_observability_overview_fetchers.test.ts | 5 +- .../apm_observability_overview_fetchers.ts | 5 +- .../apm/public/services/rest/call_api.ts | 26 +- .../public/services/rest/call_apm_api_spy.ts | 8 +- .../services/rest/create_call_apm_api.ts | 25 +- .../services/telemetry/telemetry_client.ts | 6 +- .../services/telemetry/telemetry_service.ts | 6 +- .../agent_config_instructions.test.tsx | 8 +- .../agent_config_instructions.tsx | 5 +- .../tutorial/config_agent/commands/flask.ts | 9 +- .../commands/get_apm_agent_commands.ts | 57 +- .../tutorial/config_agent/commands/node.ts | 3 +- .../tutorial/config_agent/commands/rum.ts | 6 +- .../config_agent/config_agent.stories.tsx | 4 +- .../config_agent/get_policy_options.ts | 8 +- .../tutorial/config_agent/index.test.tsx | 70 +-- .../public/tutorial/config_agent/index.tsx | 12 +- .../opentelemetry_instructions.tsx | 42 +- .../tutorial/config_agent/policy_selector.tsx | 52 +- .../tutorial/config_agent/rum_script.tsx | 7 +- .../tutorial_fleet_instructions/index.tsx | 41 +- .../apm/public/utils/download_json.ts | 4 +- .../apm/public/utils/test_helpers.tsx | 30 +- .../create_apm_users/create_apm_users_cli.ts | 29 +- .../apm/scripts/diagnostics_bundle/cli.ts | 8 +- .../diagnostics_bundle/diagnostics_bundle.ts | 19 +- .../scripts/infer_route_return_types/index.ts | 17 +- .../apm/scripts/precommit.js | 5 +- .../shared/download_telemetry_template.ts | 6 +- .../apm/scripts/shared/read_kibana_config.ts | 5 +- .../apm/scripts/telemetry/main.ts | 10 +- .../apm/scripts/test/api.js | 12 +- .../get_apm_downstream_dependencies.ts | 6 +- .../get_apm_services_list.ts | 6 +- .../assistant_functions/get_apm_timeseries.ts | 40 +- .../apm/server/assistant_functions/index.ts | 8 +- .../apm/server/deprecations/index.ts | 4 +- .../apm/server/index.ts | 29 +- .../anomaly_detection/apm_ml_anomaly_query.ts | 3 +- .../create_anomaly_detection_jobs.ts | 26 +- .../get_anomaly_timeseries.ts | 162 +++--- .../get_ml_jobs_with_apm_group.ts | 16 +- .../collect_data_telemetry/tasks.test.ts | 61 +-- .../collect_data_telemetry/tasks.ts | 326 ++++-------- .../apm/server/lib/apm_telemetry/index.ts | 9 +- .../apm/server/lib/apm_telemetry/schema.ts | 320 +++++------- .../lib/apm_telemetry/telemetry_client.ts | 31 +- .../apm/server/lib/apm_telemetry/types.ts | 5 +- ...ection_stats_items_with_relative_impact.ts | 12 +- .../get_destination_map.ts | 59 +-- .../get_connection_stats/get_stats.ts | 13 +- .../connections/get_connection_stats/index.ts | 19 +- .../create_es_client/call_async_with_debug.ts | 8 +- .../get_request_base.ts | 14 +- .../create_apm_event_client/index.test.ts | 62 +-- .../create_apm_event_client/index.ts | 66 +-- .../create_infra_metrics_client.ts | 4 +- .../create_internal_es_client/index.ts | 29 +- .../helpers/create_es_client/document_type.ts | 25 +- .../lib/helpers/get_apm_alerts_client.ts | 13 +- .../lib/helpers/get_apm_event_client.ts | 4 +- .../index.ts | 4 +- .../lib/helpers/get_document_sources.ts | 40 +- .../apm/server/lib/helpers/get_error_name.ts | 10 +- .../apm/server/lib/helpers/get_ml_client.ts | 27 +- .../lib/helpers/get_random_sampler/index.ts | 3 +- .../helpers/latency_aggregation_type/index.ts | 8 +- ...et_is_using_service_destination_metrics.ts | 76 ++- .../lib/helpers/transaction_coldstart_rate.ts | 11 +- .../lib/helpers/transaction_error_rate.ts | 12 +- .../get_is_using_transaction_events.test.ts | 5 +- .../get_is_using_transaction_events.ts | 8 +- .../server/lib/helpers/transactions/index.ts | 52 +- .../apm/server/lib/service_group_query.ts | 4 +- .../transaction_groups/get_coldstart_rate.ts | 9 +- .../get_failed_transaction_rate.ts | 21 +- .../apm/server/plugin.ts | 42 +- .../fetch_agents_latest_version.ts | 8 +- .../agent_explorer/get_agent_instances.ts | 10 +- .../routes/agent_explorer/get_agents_items.ts | 21 +- .../apm/server/routes/agent_explorer/route.ts | 23 +- .../routes/agent_keys/create_agent_key.ts | 29 +- .../routes/agent_keys/get_agent_keys.ts | 4 +- .../apm/server/routes/agent_keys/route.ts | 17 +- .../server/routes/alerts/action_variables.ts | 98 ++-- .../routes/alerts/alerting_es_client.ts | 10 +- .../routes/alerts/register_apm_rule_types.ts | 6 +- .../get_service_group_fields_for_anomaly.ts | 5 +- .../register_anomaly_rule_type.test.ts | 28 +- .../anomaly/register_anomaly_rule_type.ts | 40 +- .../get_error_count_chart_preview.ts | 51 +- .../register_error_count_rule_type.test.ts | 88 ++-- .../register_error_count_rule_type.ts | 56 +- .../rule_types/get_apm_alert_source_fields.ts | 17 +- .../average_or_percentile_agg.ts | 4 +- .../get_transaction_duration_chart_preview.ts | 64 +-- ...ter_transaction_duration_rule_type.test.ts | 21 +- ...register_transaction_duration_rule_type.ts | 66 +-- ...et_transaction_error_rate_chart_preview.ts | 63 +-- ...r_transaction_error_rate_rule_type.test.ts | 46 +- ...gister_transaction_error_rate_rule_type.ts | 71 +-- .../utils/get_groupby_action_variables.ts | 14 +- .../utils/get_groupby_terms.test.ts | 7 +- .../rule_types/utils/get_groupby_terms.ts | 5 +- .../get_global_apm_server_route_repository.ts | 10 +- .../register_apm_server_routes.test.ts | 24 +- .../apm_routes/register_apm_server_routes.ts | 29 +- .../get_apm_service_list/index.ts | 4 +- .../get_apm_service_summary/get_anomalies.ts | 192 ++++--- .../get_apm_service_summary/index.ts | 124 +++-- .../get_apm_timeseries/fetch_timeseries.ts | 5 +- .../get_error_event_rate.ts | 5 +- .../get_exit_span_failure_rate.ts | 8 +- .../get_exit_span_latency.ts | 5 +- .../get_exit_span_throughput.ts | 5 +- .../get_transaction_failure_rate.ts | 8 +- .../get_transaction_latency.ts | 10 +- .../get_transaction_throughput.ts | 5 +- .../get_log_categories/index.test.ts | 4 +- .../get_log_categories/index.ts | 16 +- .../get_container_id_from_signals.ts | 10 +- .../get_service_name_from_signals.ts | 10 +- .../routes/assistant_functions/route.ts | 39 +- .../queries/fetch_duration_correlation.ts | 10 +- ...tch_duration_correlation_with_histogram.ts | 5 +- .../fetch_duration_field_candidates.ts | 8 +- .../queries/fetch_duration_fractions.ts | 14 +- .../fetch_duration_histogram_range_steps.ts | 51 +- .../queries/fetch_duration_percentiles.ts | 5 +- .../queries/fetch_duration_ranges.ts | 4 +- ...etch_failed_events_correlation_p_values.ts | 67 +-- .../queries/fetch_field_value_pairs.ts | 45 +- .../correlations/queries/fetch_p_values.ts | 3 +- .../queries/fetch_significant_correlations.ts | 8 +- .../fetch_field_value_field_stats.ts | 40 +- .../apm/server/routes/correlations/route.ts | 28 +- .../compute_expectations_and_ranges.test.ts | 26 +- .../utils/compute_expectations_and_ranges.ts | 3 +- .../correlations/utils/get_duration_field.ts | 5 +- .../get_custom_dashboards.ts | 12 +- .../get_services_with_dashboards.ts | 15 +- .../remove_service_dashboard.ts | 10 +- .../server/routes/custom_dashboards/route.ts | 4 +- .../save_service_dashboard.ts | 5 +- .../data_view/create_static_data_view.test.ts | 23 +- .../data_view/create_static_data_view.ts | 24 +- .../get_apm_data_view_index_pattern.test.ts | 4 +- .../get_apm_data_view_index_pattern.ts | 9 +- .../apm/server/routes/data_view/route.ts | 12 +- .../get_dependency_latency_distribution.ts | 5 +- .../get_error_rate_charts_for_dependency.ts | 135 +++-- .../get_latency_charts_for_dependency.ts | 21 +- .../get_metadata_for_dependency.ts | 45 +- .../get_throughput_charts_for_dependency.ts | 98 ++-- .../dependencies/get_top_dependencies.ts | 8 +- .../get_top_dependency_operations.ts | 216 ++++---- .../dependencies/get_top_dependency_spans.ts | 12 +- .../get_upstream_services_for_dependency.ts | 4 +- .../apm/server/routes/dependencies/route.ts | 62 +-- .../diagnostics/bundle/get_apm_events.ts | 5 +- .../bundle/get_existing_index_templates.ts | 5 +- .../get_index_templates_by_index_pattern.ts | 33 +- .../routes/diagnostics/bundle/get_indices.ts | 6 +- .../diagnostics/bundle/get_indices_states.ts | 72 ++- .../diagnostics/get_diagnostics_bundle.ts | 6 +- .../helpers/get_apm_index_template_names.ts | 8 +- .../helpers/get_diagnostic_privileges.ts | 6 +- .../diagnostics/helpers/handle_exceptions.ts | 9 +- .../environments/get_all_environments.test.ts | 5 +- .../environments/get_all_environments.ts | 9 +- .../routes/environments/get_environments.ts | 14 +- .../apm/server/routes/environments/route.ts | 4 +- .../routes/errors/distribution/get_buckets.ts | 21 +- .../errors/distribution/queries.test.ts | 5 +- .../get_top_erroneous_transactions.ts | 11 +- .../get_error_group_detailed_statistics.ts | 7 +- .../get_error_group_main_statistics.ts | 120 ++--- .../apm/server/routes/errors/route.ts | 27 +- .../event_metadata/get_event_metadata.ts | 6 +- .../apm/server/routes/event_metadata/route.ts | 4 +- .../routes/fallback_to_transactions/route.ts | 3 +- .../add_api_keys_to_policies_if_missing.ts | 17 +- .../fleet/api_keys/create_apm_api_keys.ts | 85 ++- .../fleet/create_cloud_apm_package_policy.ts | 18 +- .../apm/server/routes/fleet/get_agents.ts | 10 +- .../routes/fleet/get_apm_package_policies.ts | 5 +- .../get_apm_package_policy_definition.ts | 34 +- .../fleet/get_cloud_apm_package_policy.ts | 4 +- .../routes/fleet/get_latest_apm_package.ts | 15 +- .../fleet/merge_package_policy_with_apm.ts | 9 +- .../fleet/register_fleet_policy_callbacks.ts | 16 +- .../apm/server/routes/fleet/route.ts | 47 +- .../routes/fleet/run_migration_check.ts | 15 +- .../server/routes/fleet/source_maps.test.ts | 30 +- .../apm/server/routes/fleet/source_maps.ts | 14 +- ...c_agent_configs_to_apm_package_policies.ts | 11 +- .../fleet/translate_legacy_schema_paths.ts | 20 +- .../has_historical_agent_data.ts | 11 +- .../routes/infrastructure/get_host_names.ts | 4 +- .../infrastructure/get_infrastructure_data.ts | 13 +- .../apm/server/routes/infrastructure/route.ts | 3 +- .../get_overall_latency_distribution.ts | 48 +- .../routes/latency_distribution/route.ts | 14 +- .../gc/fetch_and_transform_gc_metrics.test.ts | 6 +- .../java/gc/fetch_and_transform_gc_metrics.ts | 9 +- .../by_agent/java/gc/get_gc_rate_chart.ts | 5 +- .../by_agent/java/gc/get_gc_time_chart.ts | 5 +- .../by_agent/java/heap_memory/index.ts | 9 +- .../routes/metrics/by_agent/java/index.ts | 4 +- .../by_agent/java/non_heap_memory/index.ts | 18 +- .../metrics/by_agent/shared/memory/index.ts | 19 +- .../metrics/fetch_and_transform_metrics.ts | 14 +- .../routes/metrics/get_service_nodes.ts | 19 +- .../server/routes/metrics/has_otel_metrics.ts | 5 +- .../apm/server/routes/metrics/queries.test.ts | 5 +- .../apm/server/routes/metrics/route.ts | 7 +- .../get_active_instances_overview.ts | 97 ++-- .../get_active_instances_timeseries.ts | 11 +- .../serverless/get_cold_start_count_chart.ts | 6 +- .../get_cold_start_duration_chart.ts | 24 +- .../serverless/get_compute_usage_chart.ts | 32 +- .../get_serverless_function_latency_chart.ts | 29 +- .../get_serverless_functions_overview.ts | 18 +- .../serverless/get_serverless_summary.ts | 26 +- .../routes/metrics/serverless/helper.test.ts | 10 +- .../routes/metrics/serverless/helper.ts | 10 +- .../server/routes/metrics/serverless/route.ts | 60 +-- .../crashes/distribution/get_buckets.ts | 27 +- .../crashes/distribution/queries.test.ts | 5 +- .../get_crash_group_main_statistics.ts | 103 ++-- ..._mobile_crash_group_detailed_statistics.ts | 13 +- .../apm/server/routes/mobile/crashes/route.ts | 21 +- ..._mobile_error_group_detailed_statistics.ts | 7 +- .../get_mobile_error_group_main_statistics.ts | 107 ++-- .../get_mobile_errors_terms_by_field.ts | 53 +- .../mobile/errors/get_mobile_http_errors.ts | 21 +- .../apm/server/routes/mobile/errors/route.ts | 21 +- .../server/routes/mobile/get_device_os_app.ts | 6 +- .../mobile/get_mobile_average_launch_time.ts | 6 +- .../routes/mobile/get_mobile_crash_rate.ts | 12 +- .../mobile/get_mobile_crashes_by_location.ts | 13 +- ...get_mobile_detailed_statistics_by_field.ts | 100 ++-- .../routes/mobile/get_mobile_filters.ts | 11 +- .../routes/mobile/get_mobile_http_requests.ts | 11 +- .../get_mobile_http_requests_by_location.ts | 76 ++- .../mobile/get_mobile_launches_by_location.ts | 66 +-- .../mobile/get_mobile_location_stats.ts | 13 +- .../get_mobile_main_statistics_by_field.ts | 6 +- .../get_mobile_most_used_charts/index.ts | 26 +- .../routes/mobile/get_mobile_sessions.ts | 12 +- .../mobile/get_mobile_sessions_by_location.ts | 6 +- .../mobile/get_mobile_terms_by_field.ts | 53 +- .../apm/server/routes/mobile/get_nct.ts | 11 +- .../apm/server/routes/mobile/route.ts | 36 +- .../get_transactions_per_minute.ts | 8 +- .../routes/observability_overview/has_data.ts | 11 +- .../routes/observability_overview/route.ts | 5 +- .../routes/profiling/fetch_flamegraph.ts | 11 +- .../routes/profiling/fetch_functions.ts | 11 +- .../profiling/get_service_host_names.ts | 6 +- .../server/routes/profiling/hosts/route.ts | 69 +-- .../apm/server/routes/profiling/route.ts | 43 +- .../service_groups/delete_service_group.ts | 10 +- .../get_service_group_alerts.ts | 26 +- .../service_groups/get_service_groups.ts | 12 +- .../service_groups/get_services_counts.ts | 13 +- .../routes/service_groups/lookup_services.ts | 10 +- .../apm/server/routes/service_groups/route.ts | 26 +- .../service_groups/save_service_group.ts | 11 +- .../service_map/get_service_anomalies.ts | 24 +- .../routes/service_map/get_service_map.ts | 35 +- .../get_service_map_dependency_node_info.ts | 103 ++-- .../get_service_map_from_trace_ids.ts | 21 +- .../get_service_map_service_node_info.ts | 144 +++-- .../routes/service_map/get_service_stats.ts | 23 +- .../service_map/get_trace_sample_ids.ts | 18 +- .../service_map/group_resource_nodes.test.ts | 4 +- .../service_map/group_resource_nodes.ts | 18 +- .../apm/server/routes/service_map/route.ts | 43 +- .../transform_service_map_responses.test.ts | 23 +- .../transform_service_map_responses.ts | 104 ++-- .../get_derived_service_annotations.ts | 52 +- .../routes/services/get_service_agent.ts | 31 +- .../services/get_service_dependencies.ts | 13 +- .../get_service_dependencies_breakdown.ts | 5 +- ...get_service_instance_container_metadata.ts | 3 +- .../get_service_instance_metadata_details.ts | 25 +- .../detailed_statistics.ts | 132 +++-- ...vice_instances_system_metric_statistics.ts | 147 +++--- ...ervice_instances_transaction_statistics.ts | 125 ++--- .../services/get_service_metadata_details.ts | 45 +- .../services/get_service_metadata_icons.ts | 33 +- .../services/get_service_node_metadata.ts | 13 +- ...get_service_overview_container_metadata.ts | 16 +- ...e_transaction_group_detailed_statistics.ts | 23 +- .../get_service_transaction_groups.ts | 126 ++--- .../get_service_transaction_groups_alerts.ts | 13 +- .../services/get_service_transaction_types.ts | 10 +- .../get_services/get_service_alerts.ts | 7 +- .../get_service_names_from_terms_enum.ts | 41 +- .../get_service_transaction_stats.ts | 129 +++-- .../get_services_without_transactions.ts | 22 +- ...service_transaction_detailed_statistics.ts | 125 ++--- .../server/routes/services/get_throughput.ts | 17 +- .../apm/server/routes/services/route.ts | 169 ++---- .../create_agent_config_index.ts | 5 +- .../find_exact_configuration.ts | 22 +- .../get_agent_config_etag_metrics.ts | 21 +- .../get_agent_name_by_service.ts | 11 +- .../get_existing_environments_for_service.ts | 14 +- .../list_configurations.ts | 21 +- .../mark_applied_by_agent.ts | 5 +- .../agent_configuration/queries.test.ts | 55 +- .../settings/agent_configuration/route.ts | 34 +- .../search_configurations.ts | 5 +- .../settings/anomaly_detection/route.ts | 14 +- .../anomaly_detection/update_to_v3.ts | 3 +- .../settings/apm_indices/get_apm_indices.ts | 8 +- .../routes/settings/apm_indices/route.ts | 5 +- .../custom_link/create_custom_link_index.ts | 5 +- .../create_or_update_custom_link.test.ts | 52 +- .../create_or_update_custom_link.ts | 5 +- .../custom_link/get_transaction.test.ts | 5 +- .../settings/custom_link/helper.test.ts | 6 +- .../routes/settings/custom_link/helper.ts | 10 +- .../custom_link/list_custom_links.test.ts | 10 +- .../settings/custom_link/list_custom_links.ts | 10 +- .../routes/settings/custom_link/route.ts | 16 +- .../bulk_create_apm_source_maps.ts | 4 +- .../source_maps/create_apm_source_map.ts | 4 +- .../apm/server/routes/source_maps/route.ts | 30 +- .../schedule_source_map_migration.ts | 7 +- .../routes/span_links/get_linked_children.ts | 63 +-- .../span_links/get_span_links_details.ts | 81 ++- .../apm/server/routes/span_links/route.ts | 6 +- .../get_service_statistics.ts | 113 ++-- .../storage_explorer/get_size_timeseries.ts | 54 +- .../get_storage_details_per_service.ts | 138 ++--- .../get_summary_statistics.ts | 24 +- .../get_total_transactions_per_service.ts | 85 ++- .../has_storage_explorer_privileges.ts | 23 +- .../storage_explorer/indices_stats_helpers.ts | 4 +- .../server/routes/storage_explorer/route.ts | 103 +--- .../get_suggestions_with_terms_aggregation.ts | 64 +-- .../apm/server/routes/suggestions/route.ts | 4 +- .../routes/traces/calculate_impact_builder.ts | 4 +- .../traces/get_aggregated_critical_path.ts | 7 +- .../traces/get_top_traces_primary_stats.ts | 174 +++--- .../server/routes/traces/get_trace_items.ts | 10 +- .../traces/get_trace_samples_by_query.ts | 101 ++-- .../apm/server/routes/traces/queries.test.ts | 5 +- .../apm/server/routes/traces/route.ts | 26 +- .../transactions/breakdown/index.test.ts | 4 +- .../routes/transactions/breakdown/index.ts | 13 +- .../transactions/get_latency_charts/index.ts | 34 +- .../get_transaction_by_name/index.ts | 5 +- .../get_transaction_by_trace/index.ts | 10 +- .../routes/transactions/queries.test.ts | 5 +- .../apm/server/routes/transactions/route.ts | 171 +++--- .../transactions/trace_samples/index.ts | 10 +- .../apm/server/routes/typings.ts | 9 +- .../create_apm_users/authentication.ts | 5 +- .../create_apm_users/create_apm_users.ts | 4 +- .../helpers/create_or_update_user.ts | 4 +- .../create_apm_users/helpers/get_version.ts | 4 +- .../apm/server/tutorial/envs/elastic_cloud.ts | 23 +- .../apm/server/tutorial/envs/on_prem.ts | 59 +-- .../on_prem_apm_server_instruction_set.ts | 58 +- .../apm/server/tutorial/index.ts | 37 +- .../apm/server/types.ts | 20 +- .../apm/server/utils/test_helpers.tsx | 4 +- .../apm/typings/es_schemas/ui/fields/agent.ts | 6 +- .../apm/typings/timeseries.ts | 10 +- .../observability_onboarding/.prettierrc | 4 - .../system_logs/generate_system_logs_yml.ts | 4 +- .../common/telemetry_events.ts | 6 +- .../e2e/logs/custom_logs/configure.cy.ts | 147 ++---- .../custom_logs/install_elastic_agent.cy.ts | 240 ++------- .../e2e/cypress/e2e/logs/feedback.cy.ts | 4 +- .../e2e/cypress/e2e/logs/system_logs.cy.ts | 256 ++------- .../e2e/cypress/e2e/navigation.cy.ts | 5 +- .../e2e/cypress/support/commands.ts | 31 +- .../e2e/cypress_test_runner.ts | 10 +- .../e2e/ftr_config_runner.ts | 3 +- .../observability_onboarding/jest.config.js | 4 +- .../public/application/app.tsx | 42 +- .../experimental_onboarding_flow.tsx | 10 +- .../public/application/footer/footer.tsx | 35 +- .../public/application/header/header.tsx | 4 +- .../onboarding_flow_form.tsx | 19 +- .../use_custom_cards_for_category.ts | 27 +- .../application/packages_list/index.tsx | 28 +- .../public/application/packages_list/lazy.tsx | 9 +- .../use_integration_card_list.ts | 9 +- .../custom_logs/api_key_banner.tsx | 95 ++-- .../custom_logs/configure_logs.tsx | 115 ++-- .../custom_logs/get_filename.test.ts | 9 +- .../custom_logs/get_filename.ts | 3 +- .../quickstart_flows/custom_logs/index.tsx | 23 +- .../quickstart_flows/custom_logs/inspect.tsx | 21 +- .../custom_logs/install_elastic_agent.tsx | 128 ++--- .../shared/install_elastic_agent_steps.tsx | 60 +-- .../shared/optional_form_row.tsx | 8 +- .../shared/troubleshooting_link.tsx | 7 +- .../shared/windows_install_step.tsx | 18 +- .../quickstart_flows/system_logs/index.tsx | 16 +- .../system_logs/install_elastic_agent.tsx | 72 +-- .../system_logs/system_integration_banner.tsx | 30 +- .../app/custom_logs/api_key_banner.tsx | 95 ++-- .../app/custom_logs/configure_logs.tsx | 110 +--- .../app/custom_logs/get_filename.test.ts | 9 +- .../app/custom_logs/get_filename.ts | 3 +- .../components/app/custom_logs/index.tsx | 20 +- .../components/app/custom_logs/inspect.tsx | 20 +- .../app/custom_logs/install_elastic_agent.tsx | 151 ++---- .../app/custom_logs/select_logs.tsx | 89 +--- .../public/components/app/home/index.tsx | 116 ++-- .../components/app/system_logs/index.tsx | 12 +- .../app/system_logs/install_elastic_agent.tsx | 95 +--- .../system_logs/system_integration_banner.tsx | 30 +- .../shared/filmstrip_transition.tsx | 6 +- .../shared/install_elastic_agent_steps.tsx | 60 +-- .../components/shared/optional_form_row.tsx | 8 +- .../shared/troubleshooting_link.tsx | 7 +- .../shared/windows_install_step.tsx | 18 +- .../public/context/create_wizard_context.tsx | 20 +- .../public/hooks/use_fetcher.tsx | 41 +- .../use_flow_progress_telemetry.test.tsx | 13 +- .../hooks/use_flow_progress_telemetry.ts | 40 +- .../hooks/use_install_system_integration.ts | 6 +- .../observability_onboarding/public/index.ts | 8 +- .../onboarding_locator/locator_definition.ts | 4 +- .../locators/onboarding_locator/types.ts | 3 +- .../observability_onboarding/public/plugin.ts | 43 +- .../public/routes/index.tsx | 8 +- .../public/routes/templates/custom_logs.tsx | 42 +- .../public/routes/templates/system_logs.tsx | 24 +- .../public/services/rest/call_api.ts | 15 +- .../public/services/rest/create_call_api.ts | 20 +- .../observability_onboarding/server/index.ts | 5 +- .../server/lib/get_authentication_api_key.ts | 4 +- .../server/lib/get_fallback_urls.ts | 4 +- .../save_observability_onboarding_flow.ts | 5 +- ...e_observability_onboarding_server_route.ts | 9 +- .../server/routes/elastic_agent/route.ts | 16 +- .../server/routes/flow/route.ts | 40 +- .../server/routes/index.ts | 15 +- .../logs/api_key/create_shipper_api_key.ts | 5 +- .../api_key/has_log_monitoring_privileges.ts | 4 +- .../server/routes/logs/route.ts | 25 +- .../server/routes/register_routes.ts | 7 +- .../server/routes/types.ts | 4 +- .../observability_onboarding_status.ts | 15 +- .../services/es_legacy_config_service.ts | 4 +- .../helpers/create_custom_role.ts | 5 +- .../helpers/create_or_update_user.ts | 4 +- .../index.ts | 8 +- .../observability_onboarding/server/types.ts | 8 +- .../ux/.buildkite/pipelines/flaky.js | 12 +- .../observability_solution/ux/.prettierrc | 4 - .../ux/common/agent_name.ts | 6 +- .../ux/common/elasticsearch_fieldnames.ts | 6 +- .../ux/common/environment_filter_values.ts | 6 +- .../ux/common/environment_rt.ts | 5 +- .../ux/common/utils/merge_projection.ts | 4 +- .../ux/common/ux_ui_filter.ts | 4 +- .../ux/e2e/helpers/record_video.ts | 8 +- .../ux/e2e/helpers/synthetics_runner.ts | 9 +- .../ux/e2e/helpers/test_reporter.ts | 59 +-- .../ux/e2e/journeys/inp.journey.ts | 13 +- .../ux/e2e/journeys/page_views.ts | 4 +- .../ux/e2e/journeys/utils.ts | 42 +- .../ux/e2e/journeys/ux_js_errors.journey.ts | 4 +- .../journeys/ux_long_task_metric_journey.ts | 12 +- .../ux/e2e/page_objects/date_picker.ts | 16 +- .../ux/e2e/page_objects/login.tsx | 3 +- .../ux/e2e/synthetics_run.ts | 9 +- .../public/application/application.test.tsx | 15 +- .../ux/public/application/ux_app.tsx | 21 +- .../app/rum_dashboard/action_menu/index.tsx | 24 +- .../breakdowns/breakdown_filter.tsx | 6 +- .../app/rum_dashboard/chart_wrapper/index.tsx | 7 +- .../charts/use_exp_view_attrs.ts | 8 +- .../charts/visitor_breakdown_chart.tsx | 5 +- .../rum_dashboard/client_metrics/index.tsx | 8 +- .../rum_dashboard/client_metrics/metrics.tsx | 23 +- .../csm_shared_context/index.tsx | 15 +- .../app/rum_dashboard/empty_state_loading.tsx | 7 +- .../environment_filter/index.tsx | 11 +- .../rum_dashboard/hooks/use_has_rum_data.ts | 5 +- .../hooks/use_local_uifilters.ts | 20 +- .../impactful_metrics/js_errors.tsx | 6 +- .../rum_dashboard/local_uifilters/index.tsx | 36 +- .../rum_dashboard/local_uifilters/queries.ts | 4 +- .../local_uifilters/selected_filters.tsx | 8 +- .../local_uifilters/use_data_view.ts | 5 +- .../page_load_distribution/index.tsx | 5 +- .../percentile_annotations.tsx | 20 +- .../reset_percentile_zoom.tsx | 16 +- .../panels/web_application_select.tsx | 7 +- .../app/rum_dashboard/rum_dashboard.tsx | 10 +- .../components/app/rum_dashboard/rum_home.tsx | 27 +- .../app/rum_dashboard/translations.ts | 57 +- .../app/rum_dashboard/url_filter/index.tsx | 9 +- .../url_filter/url_search/index.tsx | 22 +- .../url_filter/url_search/render_option.tsx | 10 +- .../url_filter/url_search/use_url_search.tsx | 3 +- .../app/rum_dashboard/utils/test_helper.tsx | 5 +- .../app/rum_dashboard/ux_metrics/index.tsx | 6 +- .../ux_metrics/key_ux_metrics.test.tsx | 20 +- .../ux_metrics/key_ux_metrics.tsx | 26 +- .../rum_dashboard/ux_metrics/translations.ts | 58 +- .../app/rum_dashboard/ux_overview_fetchers.ts | 82 +-- .../rum_dashboard/visitor_breakdown/index.tsx | 25 +- .../__mocks__/regions_layer.mock.ts | 6 +- .../visitor_breakdown_map/embedded_map.tsx | 13 +- .../visitor_breakdown_map/map_tooltip.tsx | 13 +- .../visitor_breakdown_map/use_layer_list.ts | 5 +- .../visitor_breakdown_map/use_map_filters.ts | 48 +- .../context/url_params_context/helpers.ts | 5 +- .../url_params_context/resolve_url_params.ts | 8 +- .../url_params_context.test.tsx | 10 +- .../url_params_context/url_params_context.tsx | 113 ++-- .../ux/public/hooks/use_breakpoints.ts | 5 +- .../public/hooks/use_environments_fetcher.tsx | 10 +- .../ux/public/hooks/use_fetcher.tsx | 34 +- .../ux/public/hooks/use_js_errors_query.tsx | 8 +- .../ux/public/hooks/use_kibana_services.tsx | 7 +- .../hooks/use_long_task_metrics_query.tsx | 3 +- .../ux/public/plugin.ts | 14 +- .../services/data/core_web_vitals_query.ts | 3 +- .../services/data/get_es_filter.test.ts | 5 +- .../ux/public/services/data/get_es_filter.ts | 9 +- .../services/data/get_exp_view_filter.ts | 10 +- .../services/data/has_rum_data_query.ts | 9 +- .../ux/public/services/data/inp_query.ts | 6 +- .../public/services/data/js_errors_query.ts | 7 +- .../ux/public/services/data/projections.ts | 5 +- .../services/data/service_name_query.ts | 6 +- .../public/services/data/url_search_query.ts | 11 +- .../ux/public/services/rest/call_api.ts | 26 +- .../services/rest/create_call_apm_api.ts | 30 +- .../observability_solution/ux/scripts/e2e.js | 4 +- 1314 files changed, 11881 insertions(+), 25260 deletions(-) create mode 100755 .buildkite/scripts/steps/checks/prettier_topology.sh create mode 100644 scripts/prettier_topology_check.js create mode 100644 src/dev/run_prettier_topology_check.ts delete mode 100644 x-pack/plugins/canvas/.prettierrc delete mode 100644 x-pack/plugins/observability_solution/apm/.prettierrc delete mode 100644 x-pack/plugins/observability_solution/observability_onboarding/.prettierrc delete mode 100644 x-pack/plugins/observability_solution/ux/.prettierrc diff --git a/.buildkite/scripts/steps/checks/prettier_topology.sh b/.buildkite/scripts/steps/checks/prettier_topology.sh new file mode 100755 index 0000000000000..646e258572a45 --- /dev/null +++ b/.buildkite/scripts/steps/checks/prettier_topology.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +echo --- Check Prettier Configuration Topology +node scripts/prettier_topology_check diff --git a/.buildkite/scripts/steps/quick_checks.sh b/.buildkite/scripts/steps/quick_checks.sh index d96657ea70157..c9719e5c08fa9 100755 --- a/.buildkite/scripts/steps/quick_checks.sh +++ b/.buildkite/scripts/steps/quick_checks.sh @@ -22,3 +22,4 @@ export DISABLE_BOOTSTRAP_VALIDATION=false .buildkite/scripts/steps/checks/test_hardening.sh .buildkite/scripts/steps/checks/ftr_configs.sh .buildkite/scripts/steps/checks/yarn_deduplicate.sh +.buildkite/scripts/steps/checks/prettier_topology.sh diff --git a/scripts/prettier_topology_check.js b/scripts/prettier_topology_check.js new file mode 100644 index 0000000000000..49afd804681f3 --- /dev/null +++ b/scripts/prettier_topology_check.js @@ -0,0 +1,10 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_prettier_topology_check'); diff --git a/src/dev/run_prettier_topology_check.ts b/src/dev/run_prettier_topology_check.ts new file mode 100644 index 0000000000000..bc75fc8ad56b1 --- /dev/null +++ b/src/dev/run_prettier_topology_check.ts @@ -0,0 +1,56 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import globby from 'globby'; +import path from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { createFailError } from '@kbn/dev-cli-errors'; +import { run } from '@kbn/dev-cli-runner'; + +function listPaths(filePaths: string[]) { + return filePaths.map((filePath: string) => ` - ${filePath}`).join('\n'); +} + +run(async ({ log }) => { + const filePaths = await globby('**/.prettierrc*', { + cwd: REPO_ROOT, + onlyFiles: true, + gitignore: true, + ignore: [ + // the gitignore: true option makes sure that we don't + // include files from node_modules in the result, but it still + // loads all of the files from node_modules before filtering + // so it's still super slow. This prevents loading the files + // and still relies on gitignore to final ignores + '**/node_modules', + ], + }); + + // const filePaths = paths.map((path) => (new File(path)).getRelativePath()); + + if (!filePaths.length) { + throw createFailError(`A top level .prettierrc file should exist and no file was found.`); + } + + if (filePaths.length > 1) { + throw createFailError( + `Only a single .prettierrc root file should exist and more than one were found.\n${listPaths( + filePaths + )}` + ); + } + + if ( + filePaths.length === 1 && + path.resolve(path.dirname(filePaths[0])) === path.resolve(REPO_ROOT) + ) { + log.success('Only one .prettierrc file found at the root level.'); + } + + process.exit(0); +}); diff --git a/x-pack/plugins/canvas/.prettierrc b/x-pack/plugins/canvas/.prettierrc deleted file mode 100644 index f03fb6e0e808c..0000000000000 --- a/x-pack/plugins/canvas/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "singleQuote": true, - "semi": true, - "printWidth": 100, - "trailingComma": "es5" -} diff --git a/x-pack/plugins/observability_solution/apm/.prettierrc b/x-pack/plugins/observability_solution/apm/.prettierrc deleted file mode 100644 index 650cb880f6f5a..0000000000000 --- a/x-pack/plugins/observability_solution/apm/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "semi": true -} diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/configuration_types.d.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/configuration_types.d.ts index 88302dea91200..db0b4163f174d 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/configuration_types.d.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/configuration_types.d.ts @@ -8,9 +8,7 @@ import t from 'io-ts'; import { agentConfigurationIntakeRt } from './runtime_types/agent_configuration_intake_rt'; -export type AgentConfigurationIntake = t.TypeOf< - typeof agentConfigurationIntakeRt ->; +export type AgentConfigurationIntake = t.TypeOf; export type AgentConfiguration = { '@timestamp': number; diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/agent_configuration_intake_rt.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/agent_configuration_intake_rt.ts index 30cbafbd2075c..7d5b6ad12a388 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/agent_configuration_intake_rt.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/agent_configuration_intake_rt.ts @@ -10,22 +10,20 @@ import { settingDefinitions } from '../setting_definitions'; import { SettingValidation } from '../setting_definitions/types'; // retrieve validation from config definitions settings and validate on the server -const knownSettings = settingDefinitions.reduce< - Record ->((acc, { key, validation }) => { - acc[key] = validation; - return acc; -}, {}); +const knownSettings = settingDefinitions.reduce>( + (acc, { key, validation }) => { + acc[key] = validation; + return acc; + }, + {} +); export const serviceRt = t.partial({ name: t.string, environment: t.string, }); -export const settingsRt = t.intersection([ - t.record(t.string, t.string), - t.partial(knownSettings), -]); +export const settingsRt = t.intersection([t.record(t.string, t.string), t.partial(knownSettings)]); export const agentConfigurationIntakeRt = t.intersection([ t.partial({ agent_name: t.string }), diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/bytes_rt.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/bytes_rt.ts index 62d8a153179db..96ae8ba18b901 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/bytes_rt.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/bytes_rt.ts @@ -39,21 +39,14 @@ export function getBytesRt({ min, max }: { min?: string; max?: string }) { 'bytesRt', t.string.is, (input, context) => { - return either.chain( - t.string.validate(input, context), - (inputAsString) => { - const inputAsBytes = amountAndUnitToBytes(inputAsString); + return either.chain(t.string.validate(input, context), (inputAsString) => { + const inputAsBytes = amountAndUnitToBytes(inputAsString); - const isValidAmount = - inputAsBytes !== undefined && - inputAsBytes >= minAsBytes && - inputAsBytes <= maxAsBytes; + const isValidAmount = + inputAsBytes !== undefined && inputAsBytes >= minAsBytes && inputAsBytes <= maxAsBytes; - return isValidAmount - ? t.success(inputAsString) - : t.failure(input, context, message); - } - ); + return isValidAmount ? t.success(inputAsString) : t.failure(input, context, message); + }); }, t.identity ); diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/duration_rt.test.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/duration_rt.test.ts index 9ad5b62149e4b..8e711d4c85ffe 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/duration_rt.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/duration_rt.test.ts @@ -13,19 +13,7 @@ describe('getDurationRt', () => { describe('must be at least 1m', () => { const customDurationRt = getDurationRt({ min: '1m' }); describe('it should not accept', () => { - [ - undefined, - null, - '', - 0, - 'foo', - true, - false, - '0m', - '-1m', - '1ms', - '1s', - ].map((input) => { + [undefined, null, '', 0, 'foo', true, false, '0m', '-1m', '1ms', '1s'].map((input) => { it(`${JSON.stringify(input)}`, () => { expect(isRight(customDurationRt.decode(input))).toBeFalsy(); }); @@ -98,13 +86,11 @@ describe('getDurationRt', () => { const customDurationRt = getDurationRt({ max: '1m' }); describe('it should not accept', () => { - [undefined, null, '', 0, 'foo', true, false, '2m', '61s', '60001ms'].map( - (input) => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeFalsy(); - }); - } - ); + [undefined, null, '', 0, 'foo', true, false, '2m', '61s', '60001ms'].map((input) => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + }); }); describe('it should return correct error message', () => { ['2m', '61s', '60001ms'].map((input) => { diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/duration_rt.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/duration_rt.ts index bfc1d9b26f8fa..c968a7cacebc4 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/duration_rt.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/duration_rt.ts @@ -33,22 +33,16 @@ export function getDurationRt({ min, max }: { min?: string; max?: string }) { 'durationRt', t.string.is, (input, context) => { - return either.chain( - t.string.validate(input, context), - (inputAsString) => { - const inputAsMilliseconds = - amountAndUnitToMilliseconds(inputAsString); + return either.chain(t.string.validate(input, context), (inputAsString) => { + const inputAsMilliseconds = amountAndUnitToMilliseconds(inputAsString); - const isValidAmount = - inputAsMilliseconds !== undefined && - inputAsMilliseconds >= minAsMilliseconds && - inputAsMilliseconds <= maxAsMilliseconds; + const isValidAmount = + inputAsMilliseconds !== undefined && + inputAsMilliseconds >= minAsMilliseconds && + inputAsMilliseconds <= maxAsMilliseconds; - return isValidAmount - ? t.success(inputAsString) - : t.failure(input, context, message); - } - ); + return isValidAmount ? t.success(inputAsString) : t.failure(input, context, message); + }); }, t.identity ); diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/float_rt.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/float_rt.ts index ba026b0579d14..a0e2d3f5b60ba 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/float_rt.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/float_rt.ts @@ -14,11 +14,9 @@ export const floatRt = new t.Type( (input, context) => { return either.chain(t.string.validate(input, context), (inputAsString) => { const inputAsFloat = parseFloat(inputAsString); - const maxThreeDecimals = - parseFloat(inputAsFloat.toFixed(3)) === inputAsFloat; + const maxThreeDecimals = parseFloat(inputAsFloat.toFixed(3)) === inputAsFloat; - const isValid = - inputAsFloat >= 0 && inputAsFloat <= 1 && maxThreeDecimals; + const isValid = inputAsFloat >= 0 && inputAsFloat <= 1 && maxThreeDecimals; return isValid ? t.success(inputAsString) diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/get_range_type_message.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/get_range_type_message.ts index 855f2276b6eb5..d4efd93126947 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/get_range_type_message.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/get_range_type_message.ts @@ -19,10 +19,7 @@ function getRangeType(min?: number, max?: number) { } } -export function getRangeTypeMessage( - min?: number | string, - max?: number | string -) { +export function getRangeTypeMessage(min?: number | string, max?: number | string) { return i18n.translate('xpack.apm.agentConfig.range.errorText', { defaultMessage: `{rangeType, select, between {Must be between {min} and {max}} diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/integer_rt.test.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/integer_rt.test.ts index 34809c4c0f9dd..6d9dffca8ab61 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/integer_rt.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/integer_rt.test.ts @@ -17,13 +17,11 @@ describe('getIntegerRt', () => { }); describe('it should not accept', () => { - [NaN, undefined, null, '', 'foo', 0, 55, '-1', '-55', '33000'].map( - (input) => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(integerRt.decode(input))).toBe(false); - }); - } - ); + [NaN, undefined, null, '', 'foo', 0, 55, '-1', '-55', '33000'].map((input) => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(integerRt.decode(input))).toBe(false); + }); + }); }); describe('it should return correct error message', () => { diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/integer_rt.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/integer_rt.ts index e451cfc7102e3..e5fd4435ee2b3 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/integer_rt.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/integer_rt.ts @@ -22,16 +22,11 @@ export function getIntegerRt({ 'integerRt', t.string.is, (input, context) => { - return either.chain( - t.string.validate(input, context), - (inputAsString) => { - const inputAsInt = parseInt(inputAsString, 10); - const isValid = inputAsInt >= min && inputAsInt <= max; - return isValid - ? t.success(inputAsString) - : t.failure(input, context, message); - } - ); + return either.chain(t.string.validate(input, context), (inputAsString) => { + const inputAsInt = parseInt(inputAsString, 10); + const isValid = inputAsInt >= min && inputAsInt <= max; + return isValid ? t.success(inputAsString) : t.failure(input, context, message); + }); }, t.identity ); diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/storage_size_rt.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/storage_size_rt.ts index 491a274316c3b..57649f9746417 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/storage_size_rt.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/runtime_types/storage_size_rt.ts @@ -38,34 +38,25 @@ function amountAndUnitToBytes({ } export function getStorageSizeRt({ min, max }: { min?: string; max?: string }) { - const minAsBytes = - amountAndUnitToBytes({ value: min, decimalUnitBase: true }) ?? -Infinity; - const maxAsBytes = - amountAndUnitToBytes({ value: max, decimalUnitBase: true }) ?? Infinity; + const minAsBytes = amountAndUnitToBytes({ value: min, decimalUnitBase: true }) ?? -Infinity; + const maxAsBytes = amountAndUnitToBytes({ value: max, decimalUnitBase: true }) ?? Infinity; const message = getRangeTypeMessage(min, max); return new t.Type( 'storageSizeRt', t.string.is, (input, context) => { - return either.chain( - t.string.validate(input, context), - (inputAsString) => { - const inputAsBytes = amountAndUnitToBytes({ - value: inputAsString, - decimalUnitBase: true, - }); + return either.chain(t.string.validate(input, context), (inputAsString) => { + const inputAsBytes = amountAndUnitToBytes({ + value: inputAsString, + decimalUnitBase: true, + }); - const isValidAmount = - inputAsBytes !== undefined && - inputAsBytes >= minAsBytes && - inputAsBytes <= maxAsBytes; + const isValidAmount = + inputAsBytes !== undefined && inputAsBytes >= minAsBytes && inputAsBytes <= maxAsBytes; - return isValidAmount - ? t.success(inputAsString) - : t.failure(input, context, message); - } - ); + return isValidAmount ? t.success(inputAsString) : t.failure(input, context, message); + }); }, t.identity ); diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/general_settings.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/general_settings.ts index 08fd02770ba5b..e7d04ffa3d97c 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/general_settings.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/general_settings.ts @@ -21,13 +21,10 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.apiRequestSize.label', { defaultMessage: 'API Request Size', }), - description: i18n.translate( - 'xpack.apm.agentConfig.apiRequestSize.description', - { - defaultMessage: - 'The maximum total compressed size of the request body which is sent to the APM Server intake api via a chunked encoding (HTTP streaming).\nNote that a small overshoot is possible.\n\nAllowed byte units are `b`, `kb` and `mb`. `1kb` is equal to `1024b`.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.apiRequestSize.description', { + defaultMessage: + 'The maximum total compressed size of the request body which is sent to the APM Server intake api via a chunked encoding (HTTP streaming).\nNote that a small overshoot is possible.\n\nAllowed byte units are `b`, `kb` and `mb`. `1kb` is equal to `1024b`.', + }), excludeAgents: [ 'js-base', 'rum-js', @@ -48,13 +45,10 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.apiRequestTime.label', { defaultMessage: 'API Request Time', }), - description: i18n.translate( - 'xpack.apm.agentConfig.apiRequestTime.description', - { - defaultMessage: - "Maximum time to keep an HTTP request to the APM Server open for.\n\nNOTE: This value has to be lower than the APM Server's `read_timeout` setting.", - } - ), + description: i18n.translate('xpack.apm.agentConfig.apiRequestTime.description', { + defaultMessage: + "Maximum time to keep an HTTP request to the APM Server open for.\n\nNOTE: This value has to be lower than the APM Server's `read_timeout` setting.", + }), excludeAgents: [ 'js-base', 'rum-js', @@ -76,13 +70,10 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.captureBody.label', { defaultMessage: 'Capture body', }), - description: i18n.translate( - 'xpack.apm.agentConfig.captureBody.description', - { - defaultMessage: - 'For transactions that are HTTP requests, the agent can optionally capture the request body (e.g. POST variables).\nFor transactions that are initiated by receiving a message from a message broker, the agent can capture the textual message body.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.captureBody.description', { + defaultMessage: + 'For transactions that are HTTP requests, the agent can optionally capture the request body (e.g. POST variables).\nFor transactions that are initiated by receiving a message from a message broker, the agent can capture the textual message body.', + }), options: [ { text: 'off', value: 'off' }, { text: 'errors', value: 'errors' }, @@ -95,23 +86,16 @@ export const generalSettings: RawSettingDefinition[] = [ { key: 'capture_body_content_types', type: 'text', - defaultValue: - 'application/x-www-form-urlencoded*, text/*, application/json*, application/xml*', - label: i18n.translate( - 'xpack.apm.agentConfig.captureBodyContentTypes.label', - { - defaultMessage: 'Capture Body Content Types', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.captureBodyContentTypes.description', - { - defaultMessage: - 'Configures which content types should be recorded.\n' + - '\n' + - 'The defaults end with a wildcard so that content types like `text/plain; charset=utf-8` are captured as well.', - } - ), + defaultValue: 'application/x-www-form-urlencoded*, text/*, application/json*, application/xml*', + label: i18n.translate('xpack.apm.agentConfig.captureBodyContentTypes.label', { + defaultMessage: 'Capture Body Content Types', + }), + description: i18n.translate('xpack.apm.agentConfig.captureBodyContentTypes.description', { + defaultMessage: + 'Configures which content types should be recorded.\n' + + '\n' + + 'The defaults end with a wildcard so that content types like `text/plain; charset=utf-8` are captured as well.', + }), includeAgents: ['java', 'dotnet'], }, @@ -123,21 +107,11 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.captureHeaders.label', { defaultMessage: 'Capture Headers', }), - description: i18n.translate( - 'xpack.apm.agentConfig.captureHeaders.description', - { - defaultMessage: - 'If set to `true`, the agent will capture HTTP request and response headers (including cookies), as well as message headers/properties when using messaging frameworks (like Kafka).\n\nNOTE: Setting this to `false` reduces network bandwidth, disk space and object allocations.', - } - ), - excludeAgents: [ - 'js-base', - 'rum-js', - 'nodejs', - 'php', - 'android/java', - 'iOS/swift', - ], + description: i18n.translate('xpack.apm.agentConfig.captureHeaders.description', { + defaultMessage: + 'If set to `true`, the agent will capture HTTP request and response headers (including cookies), as well as message headers/properties when using messaging frameworks (like Kafka).\n\nNOTE: Setting this to `false` reduces network bandwidth, disk space and object allocations.', + }), + excludeAgents: ['js-base', 'rum-js', 'nodejs', 'php', 'android/java', 'iOS/swift'], }, { @@ -147,17 +121,14 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.dedotCustomMetrics.label', { defaultMessage: 'Dedot custom metrics', }), - description: i18n.translate( - 'xpack.apm.agentConfig.dedotCustomMetrics.description', - { - defaultMessage: - 'Replaces dots with underscores in the metric names for custom metrics.\n' + - '\n' + - 'WARNING: Setting this to `false` can lead to mapping conflicts as dots indicate nesting in Elasticsearch.\n' + - 'An example of when a conflict happens is two metrics with the name `foo` and `foo.bar`.\n' + - 'The first metric maps `foo` to a number and the second metric maps `foo` as an object.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.dedotCustomMetrics.description', { + defaultMessage: + 'Replaces dots with underscores in the metric names for custom metrics.\n' + + '\n' + + 'WARNING: Setting this to `false` can lead to mapping conflicts as dots indicate nesting in Elasticsearch.\n' + + 'An example of when a conflict happens is two metrics with the name `foo` and `foo.bar`.\n' + + 'The first metric maps `foo` to a number and the second metric maps `foo` as an object.', + }), includeAgents: ['java'], }, @@ -165,24 +136,18 @@ export const generalSettings: RawSettingDefinition[] = [ key: 'disable_instrumentations', type: 'text', defaultValue: '', - label: i18n.translate( - 'xpack.apm.agentConfig.disableInstrumentations.label', - { - defaultMessage: 'Disable instrumentations', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.disableInstrumentations.description', - { - defaultMessage: - 'Comma-separated list of modules to disable instrumentation for.\n' + - 'When instrumentation is disabled for a module, no spans will be collected for that module.\n' + - '\n' + - 'The up-to-date list of modules for which instrumentation can be disabled is language specific ' + - 'and can be found under the following links: ' + - '[Java](https://www.elastic.co/guide/en/apm/agent/java/current/config-core.html#config-disable-instrumentations)', - } - ), + label: i18n.translate('xpack.apm.agentConfig.disableInstrumentations.label', { + defaultMessage: 'Disable instrumentations', + }), + description: i18n.translate('xpack.apm.agentConfig.disableInstrumentations.description', { + defaultMessage: + 'Comma-separated list of modules to disable instrumentation for.\n' + + 'When instrumentation is disabled for a module, no spans will be collected for that module.\n' + + '\n' + + 'The up-to-date list of modules for which instrumentation can be disabled is language specific ' + + 'and can be found under the following links: ' + + '[Java](https://www.elastic.co/guide/en/apm/agent/java/current/config-core.html#config-disable-instrumentations)', + }), includeAgents: ['java'], }, @@ -190,12 +155,9 @@ export const generalSettings: RawSettingDefinition[] = [ key: 'disable_outgoing_tracecontext_headers', type: 'boolean', defaultValue: 'true', - label: i18n.translate( - 'xpack.apm.agentConfig.disableOutgoingTracecontextHeaders.label', - { - defaultMessage: 'Disable outgoing tracecontext headers', - } - ), + label: i18n.translate('xpack.apm.agentConfig.disableOutgoingTracecontextHeaders.label', { + defaultMessage: 'Disable outgoing tracecontext headers', + }), description: i18n.translate( 'xpack.apm.agentConfig.disableOutgoingTracecontextHeaders.description', { @@ -216,15 +178,12 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.exitSpanMinDuration.label', { defaultMessage: 'Exit span min duration', }), - description: i18n.translate( - 'xpack.apm.agentConfig.exitSpanMinDuration.description', - { - defaultMessage: - 'Exit spans are spans that represent a call to an external service, like a database. If such calls are very short, they are usually not relevant and can be ignored.\n' + - '\n' + - 'NOTE: If a span propagates distributed tracing ids, it will not be ignored, even if it is shorter than the configured threshold. This is to ensure that no broken traces are recorded.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.exitSpanMinDuration.description', { + defaultMessage: + 'Exit spans are spans that represent a call to an external service, like a database. If such calls are very short, they are usually not relevant and can be ignored.\n' + + '\n' + + 'NOTE: If a span propagates distributed tracing ids, it will not be ignored, even if it is shorter than the configured threshold. This is to ensure that no broken traces are recorded.', + }), includeAgents: ['java', 'dotnet', 'nodejs', 'python'], }, @@ -235,16 +194,13 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.ignoreMessageQueues.label', { defaultMessage: 'Ignore message queues', }), - description: i18n.translate( - 'xpack.apm.agentConfig.ignoreMessageQueues.description', - { - defaultMessage: - 'Used to filter out specific messaging queues/topics from being traced. \n' + - '\n' + - 'This property should be set to an array containing one or more strings.\n' + - 'When set, sends-to and receives-from the specified queues/topic will be ignored.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.ignoreMessageQueues.description', { + defaultMessage: + 'Used to filter out specific messaging queues/topics from being traced. \n' + + '\n' + + 'This property should be set to an array containing one or more strings.\n' + + 'When set, sends-to and receives-from the specified queues/topic will be ignored.', + }), includeAgents: ['java', 'dotnet', 'nodejs'], }, @@ -256,15 +212,12 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.logEcsReformatting.label', { defaultMessage: 'Log ECS reformatting', }), - description: i18n.translate( - 'xpack.apm.agentConfig.logEcsReformatting.description', - { - defaultMessage: - 'Specifying whether and how the agent should automatically reformat application logs into ' + - '[ECS-compatible JSON](https://www.elastic.co/guide/en/ecs-logging/overview/master/intro.html), ' + - 'suitable for ingestion into Elasticsearch for further Log analysis.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.logEcsReformatting.description', { + defaultMessage: + 'Specifying whether and how the agent should automatically reformat application logs into ' + + '[ECS-compatible JSON](https://www.elastic.co/guide/en/ecs-logging/overview/master/intro.html), ' + + 'suitable for ingestion into Elasticsearch for further Log analysis.', + }), options: [ { text: 'off', value: 'off' }, { text: 'shade', value: 'shade' }, @@ -302,12 +255,9 @@ export const generalSettings: RawSettingDefinition[] = [ key: 'mongodb_capture_statement_commands', type: 'boolean', defaultValue: 'false', - label: i18n.translate( - 'xpack.apm.agentConfig.mongodbCaptureStatementCommands.label', - { - defaultMessage: 'MongoDB capture statement commands', - } - ), + label: i18n.translate('xpack.apm.agentConfig.mongodbCaptureStatementCommands.label', { + defaultMessage: 'MongoDB capture statement commands', + }), description: i18n.translate( 'xpack.apm.agentConfig.mongodbCaptureStatementCommands.description', { @@ -341,19 +291,13 @@ export const generalSettings: RawSettingDefinition[] = [ key: 'context_propagation_only', type: 'boolean', defaultValue: 'false', - label: i18n.translate( - 'xpack.apm.agentConfig.context_propagation_only.label', - { - defaultMessage: 'Context Propagation Only', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.context_propagation_only.description', - { - defaultMessage: - 'When set to true, disables log sending, metrics and trace collection. Trace context propagation and log correlation will stay active.', - } - ), + label: i18n.translate('xpack.apm.agentConfig.context_propagation_only.label', { + defaultMessage: 'Context Propagation Only', + }), + description: i18n.translate('xpack.apm.agentConfig.context_propagation_only.description', { + defaultMessage: + 'When set to true, disables log sending, metrics and trace collection. Trace context propagation and log correlation will stay active.', + }), includeAgents: ['java'], }, @@ -365,13 +309,10 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.serverTimeout.label', { defaultMessage: 'Server Timeout', }), - description: i18n.translate( - 'xpack.apm.agentConfig.serverTimeout.description', - { - defaultMessage: - 'If a request to the APM Server takes longer than the configured timeout,\nthe request is cancelled and the event (exception or transaction) is discarded.\nSet to 0 to disable timeouts.\n\nWARNING: If timeouts are disabled or set to a high value, your app could experience memory issues if the APM Server times out.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.serverTimeout.description', { + defaultMessage: + 'If a request to the APM Server takes longer than the configured timeout,\nthe request is cancelled and the event (exception or transaction) is discarded.\nSet to 0 to disable timeouts.\n\nWARNING: If timeouts are disabled or set to a high value, your app could experience memory issues if the APM Server times out.', + }), includeAgents: ['java'], }, @@ -379,20 +320,14 @@ export const generalSettings: RawSettingDefinition[] = [ key: 'span_compression_enabled', type: 'boolean', defaultValue: 'true', - label: i18n.translate( - 'xpack.apm.agentConfig.spanCompressionEnabled.label', - { - defaultMessage: 'Span compression enabled', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.spanCompressionEnabled.description', - { - defaultMessage: - 'Setting this option to true will enable span compression feature.\n' + - 'Span compression reduces the collection, processing, and storage overhead, and removes clutter from the UI. The tradeoff is that some information such as DB statements of all the compressed spans will not be collected.', - } - ), + label: i18n.translate('xpack.apm.agentConfig.spanCompressionEnabled.label', { + defaultMessage: 'Span compression enabled', + }), + description: i18n.translate('xpack.apm.agentConfig.spanCompressionEnabled.description', { + defaultMessage: + 'Setting this option to true will enable span compression feature.\n' + + 'Span compression reduces the collection, processing, and storage overhead, and removes clutter from the UI. The tradeoff is that some information such as DB statements of all the compressed spans will not be collected.', + }), includeAgents: ['java', 'dotnet', 'python'], }, @@ -401,12 +336,9 @@ export const generalSettings: RawSettingDefinition[] = [ type: 'duration', defaultValue: '50ms', min: '0ms', - label: i18n.translate( - 'xpack.apm.agentConfig.spanCompressionExactMatchMaxDuration.label', - { - defaultMessage: 'Span compression exact match max duration', - } - ), + label: i18n.translate('xpack.apm.agentConfig.spanCompressionExactMatchMaxDuration.label', { + defaultMessage: 'Span compression exact match max duration', + }), description: i18n.translate( 'xpack.apm.agentConfig.spanCompressionExactMatchMaxDuration.description', { @@ -421,12 +353,9 @@ export const generalSettings: RawSettingDefinition[] = [ type: 'duration', defaultValue: '0ms', min: '0ms', - label: i18n.translate( - 'xpack.apm.agentConfig.spanCompressionSameKindMaxDuration.label', - { - defaultMessage: 'Span compression same kind max duration', - } - ), + label: i18n.translate('xpack.apm.agentConfig.spanCompressionSameKindMaxDuration.label', { + defaultMessage: 'Span compression same kind max duration', + }), description: i18n.translate( 'xpack.apm.agentConfig.spanCompressionSameKindMaxDuration.description', { @@ -446,21 +375,11 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.spanFramesMinDuration.label', { defaultMessage: 'Span frames minimum duration', }), - description: i18n.translate( - 'xpack.apm.agentConfig.spanFramesMinDuration.description', - { - defaultMessage: - '(Deprecated, use `span_stack_trace_min_duration` instead!) In its default settings, the APM agent will collect a stack trace with every recorded span.\nWhile this is very helpful to find the exact place in your code that causes the span, collecting this stack trace does have some overhead. \nWhen setting this option to a negative value, like `-1ms`, stack traces will be collected for all spans. Setting it to a positive value, e.g. `5ms`, will limit stack trace collection to spans with durations equal to or longer than the given value, e.g. 5 milliseconds.\n\nTo disable stack trace collection for spans completely, set the value to `0ms`.', - } - ), - excludeAgents: [ - 'js-base', - 'rum-js', - 'nodejs', - 'php', - 'android/java', - 'iOS/swift', - ], + description: i18n.translate('xpack.apm.agentConfig.spanFramesMinDuration.description', { + defaultMessage: + '(Deprecated, use `span_stack_trace_min_duration` instead!) In its default settings, the APM agent will collect a stack trace with every recorded span.\nWhile this is very helpful to find the exact place in your code that causes the span, collecting this stack trace does have some overhead. \nWhen setting this option to a negative value, like `-1ms`, stack traces will be collected for all spans. Setting it to a positive value, e.g. `5ms`, will limit stack trace collection to spans with durations equal to or longer than the given value, e.g. 5 milliseconds.\n\nTo disable stack trace collection for spans completely, set the value to `0ms`.', + }), + excludeAgents: ['js-base', 'rum-js', 'nodejs', 'php', 'android/java', 'iOS/swift'], }, { @@ -468,24 +387,18 @@ export const generalSettings: RawSettingDefinition[] = [ type: 'duration', min: '-1ms', defaultValue: '5ms', - label: i18n.translate( - 'xpack.apm.agentConfig.spanStackTraceMinDuration.label', - { - defaultMessage: 'Span stack trace minimum duration', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.spanStackTraceMinDuration.description', - { - defaultMessage: - 'While this is very helpful to find the exact place in your code that causes the span, ' + - 'collecting this stack trace does have some overhead. When setting this option to the value `0ms`, ' + - 'stack traces will be collected for all spans. Setting it to a positive value, e.g. `5ms`, will limit ' + - 'stack trace collection to spans with durations equal to or longer than the given value, e.g. 5 milliseconds.\n' + - '\n' + - 'To disable stack trace collection for spans completely, set the value to `-1ms`.', - } - ), + label: i18n.translate('xpack.apm.agentConfig.spanStackTraceMinDuration.label', { + defaultMessage: 'Span stack trace minimum duration', + }), + description: i18n.translate('xpack.apm.agentConfig.spanStackTraceMinDuration.description', { + defaultMessage: + 'While this is very helpful to find the exact place in your code that causes the span, ' + + 'collecting this stack trace does have some overhead. When setting this option to the value `0ms`, ' + + 'stack traces will be collected for all spans. Setting it to a positive value, e.g. `5ms`, will limit ' + + 'stack trace collection to spans with durations equal to or longer than the given value, e.g. 5 milliseconds.\n' + + '\n' + + 'To disable stack trace collection for spans completely, set the value to `-1ms`.', + }), includeAgents: ['java', 'dotnet', 'nodejs', 'python'], }, @@ -497,13 +410,10 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.stackTraceLimit.label', { defaultMessage: 'Stack trace limit', }), - description: i18n.translate( - 'xpack.apm.agentConfig.stackTraceLimit.description', - { - defaultMessage: - 'Setting it to 0 will disable stack trace collection. Any positive integer value will be used as the maximum number of frames to collect. Setting it -1 means that all frames will be collected.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.stackTraceLimit.description', { + defaultMessage: + 'Setting it to 0 will disable stack trace collection. Any positive integer value will be used as the maximum number of frames to collect. Setting it -1 means that all frames will be collected.', + }), includeAgents: ['java', 'dotnet', 'go', 'python'], }, @@ -512,31 +422,25 @@ export const generalSettings: RawSettingDefinition[] = [ validation: traceContinuationStrategyRt, type: 'select', defaultValue: 'continue', - label: i18n.translate( - 'xpack.apm.agentConfig.traceContinuationStrategy.label', - { - defaultMessage: 'Trace continuation strategy', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.traceContinuationStrategy.description', - { - defaultMessage: - 'This option allows some control over how the APM agent handles W3C trace-context headers on incoming requests. By default, the `traceparent` and `tracestate` headers are used per W3C spec for distributed tracing. However, in certain cases it can be helpful to not use the incoming `traceparent` header. Some example use cases:\n' + - '\n' + - '* An Elastic-monitored service is receiving requests with `traceparent` headers from unmonitored services.\n' + - '* An Elastic-monitored service is publicly exposed, and does not want tracing data (trace-ids, sampling decisions) to possibly be spoofed by user requests.\n' + - '\n' + - 'Valid values are:\n' + - "* 'continue': The default behavior. An incoming `traceparent` value is used to continue the trace and determine the sampling decision.\n" + - "* 'restart': Always ignores the `traceparent` header of incoming requests. A new trace-id will be generated and the sampling decision will be made based on transaction_sample_rate. A span link will be made to the incoming `traceparent`.\n" + - "* 'restart_external': If an incoming request includes the `es` vendor flag in `tracestate`, then any `traceparent` will be considered internal and will be handled as described for 'continue' above. Otherwise, any `traceparent` is considered external and will be handled as described for 'restart' above.\n" + - '\n' + - 'Starting with Elastic Observability 8.2, span links are visible in trace views.\n' + - '\n' + - 'This option is case-insensitive.', - } - ), + label: i18n.translate('xpack.apm.agentConfig.traceContinuationStrategy.label', { + defaultMessage: 'Trace continuation strategy', + }), + description: i18n.translate('xpack.apm.agentConfig.traceContinuationStrategy.description', { + defaultMessage: + 'This option allows some control over how the APM agent handles W3C trace-context headers on incoming requests. By default, the `traceparent` and `tracestate` headers are used per W3C spec for distributed tracing. However, in certain cases it can be helpful to not use the incoming `traceparent` header. Some example use cases:\n' + + '\n' + + '* An Elastic-monitored service is receiving requests with `traceparent` headers from unmonitored services.\n' + + '* An Elastic-monitored service is publicly exposed, and does not want tracing data (trace-ids, sampling decisions) to possibly be spoofed by user requests.\n' + + '\n' + + 'Valid values are:\n' + + "* 'continue': The default behavior. An incoming `traceparent` value is used to continue the trace and determine the sampling decision.\n" + + "* 'restart': Always ignores the `traceparent` header of incoming requests. A new trace-id will be generated and the sampling decision will be made based on transaction_sample_rate. A span link will be made to the incoming `traceparent`.\n" + + "* 'restart_external': If an incoming request includes the `es` vendor flag in `tracestate`, then any `traceparent` will be considered internal and will be handled as described for 'continue' above. Otherwise, any `traceparent` is considered external and will be handled as described for 'restart' above.\n" + + '\n' + + 'Starting with Elastic Observability 8.2, span links are visible in trace views.\n' + + '\n' + + 'This option is case-insensitive.', + }), options: [ { text: 'continue', value: 'continue' }, { text: 'restart', value: 'restart' }, @@ -554,13 +458,9 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.transactionMaxSpans.label', { defaultMessage: 'Transaction max spans', }), - description: i18n.translate( - 'xpack.apm.agentConfig.transactionMaxSpans.description', - { - defaultMessage: - 'Limits the amount of spans that are recorded per transaction.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.transactionMaxSpans.description', { + defaultMessage: 'Limits the amount of spans that are recorded per transaction.', + }), excludeAgents: ['js-base', 'rum-js', 'android/java', 'iOS/swift'], }, @@ -572,13 +472,10 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.transactionSampleRate.label', { defaultMessage: 'Transaction sample rate', }), - description: i18n.translate( - 'xpack.apm.agentConfig.transactionSampleRate.description', - { - defaultMessage: - 'By default, the agent will sample every transaction (e.g. request to your service). To reduce overhead and storage requirements, you can set the sample rate to a value between 0.0 and 1.0. We still record overall time and the result for unsampled transactions, but not context information, labels, or spans.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.transactionSampleRate.description', { + defaultMessage: + 'By default, the agent will sample every transaction (e.g. request to your service). To reduce overhead and storage requirements, you can set the sample rate to a value between 0.0 and 1.0. We still record overall time and the result for unsampled transactions, but not context information, labels, or spans.', + }), excludeAgents: ['android/java', 'iOS/swift'], }, @@ -591,13 +488,10 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.sanitizeFiledNames.label', { defaultMessage: 'Sanitize field names', }), - description: i18n.translate( - 'xpack.apm.agentConfig.sanitizeFiledNames.description', - { - defaultMessage: - 'Sometimes it is necessary to sanitize, i.e., remove, sensitive data sent to Elastic APM. This config accepts a list of wildcard patterns of field names which should be sanitized. These apply to HTTP headers (including cookies) and `application/x-www-form-urlencoded` data (POST form fields). The query string and the captured request body (such as `application/json` data) will not get sanitized.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.sanitizeFiledNames.description', { + defaultMessage: + 'Sometimes it is necessary to sanitize, i.e., remove, sensitive data sent to Elastic APM. This config accepts a list of wildcard patterns of field names which should be sanitized. These apply to HTTP headers (including cookies) and `application/x-www-form-urlencoded` data (POST form fields). The query string and the captured request body (such as `application/json` data) will not get sanitized.', + }), includeAgents: ['java', 'python', 'go', 'dotnet', 'nodejs', 'ruby'], }, @@ -610,13 +504,10 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.transactionIgnoreUrl.label', { defaultMessage: 'Ignore transactions based on URLs', }), - description: i18n.translate( - 'xpack.apm.agentConfig.transactionIgnoreUrl.description', - { - defaultMessage: - 'Used to restrict requests to certain URLs from being instrumented. This config accepts a comma-separated list of wildcard patterns of URL paths that should be ignored. When an incoming HTTP request is detected, its request path will be tested against each element in this list. For example, adding `/home/index` to this list would match and remove instrumentation from `http://localhost/home/index` as well as `http://whatever.com/home/index?value1=123`', - } - ), + description: i18n.translate('xpack.apm.agentConfig.transactionIgnoreUrl.description', { + defaultMessage: + 'Used to restrict requests to certain URLs from being instrumented. This config accepts a comma-separated list of wildcard patterns of URL paths that should be ignored. When an incoming HTTP request is detected, its request path will be tested against each element in this list. For example, adding `/home/index` to this list would match and remove instrumentation from `http://localhost/home/index` as well as `http://whatever.com/home/index?value1=123`', + }), includeAgents: ['java', 'nodejs', 'python', 'dotnet', 'ruby', 'go'], }, @@ -624,23 +515,17 @@ export const generalSettings: RawSettingDefinition[] = [ key: 'transaction_ignore_user_agents', type: 'text', defaultValue: '', - label: i18n.translate( - 'xpack.apm.agentConfig.transactionIgnoreUserAgents.label', - { - defaultMessage: 'Transaction ignore user agents', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.transactionIgnoreUserAgents.description', - { - defaultMessage: - 'Used to restrict requests from certain User-Agents from being instrumented.\n' + - '\n' + - 'When an incoming HTTP request is detected,\n' + - 'the User-Agent from the request headers will be tested against each element in this list.\n' + - 'Example: `curl/*`, `*pingdom*`', - } - ), + label: i18n.translate('xpack.apm.agentConfig.transactionIgnoreUserAgents.label', { + defaultMessage: 'Transaction ignore user agents', + }), + description: i18n.translate('xpack.apm.agentConfig.transactionIgnoreUserAgents.description', { + defaultMessage: + 'Used to restrict requests from certain User-Agents from being instrumented.\n' + + '\n' + + 'When an incoming HTTP request is detected,\n' + + 'the User-Agent from the request headers will be tested against each element in this list.\n' + + 'Example: `curl/*`, `*pingdom*`', + }), includeAgents: ['java'], }, @@ -648,25 +533,19 @@ export const generalSettings: RawSettingDefinition[] = [ key: 'use_path_as_transaction_name', type: 'boolean', defaultValue: 'false', - label: i18n.translate( - 'xpack.apm.agentConfig.usePathAsTransactionName.label', - { - defaultMessage: 'Use path as transaction name', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.usePathAsTransactionName.description', - { - defaultMessage: - 'If set to `true`,\n' + - 'transaction names of unsupported or partially-supported frameworks will be in the form of `$method $path` instead of just `$method unknown route`.\n' + - '\n' + - 'WARNING: If your URLs contain path parameters like `/user/$userId`,\n' + - 'you should be very careful when enabling this flag,\n' + - 'as it can lead to an explosion of transaction groups.\n' + - 'Take a look at the `transaction_name_groups` option on how to mitigate this problem by grouping URLs together.', - } - ), + label: i18n.translate('xpack.apm.agentConfig.usePathAsTransactionName.label', { + defaultMessage: 'Use path as transaction name', + }), + description: i18n.translate('xpack.apm.agentConfig.usePathAsTransactionName.description', { + defaultMessage: + 'If set to `true`,\n' + + 'transaction names of unsupported or partially-supported frameworks will be in the form of `$method $path` instead of just `$method unknown route`.\n' + + '\n' + + 'WARNING: If your URLs contain path parameters like `/user/$userId`,\n' + + 'you should be very careful when enabling this flag,\n' + + 'as it can lead to an explosion of transaction groups.\n' + + 'Take a look at the `transaction_name_groups` option on how to mitigate this problem by grouping URLs together.', + }), includeAgents: ['java'], }, @@ -677,18 +556,15 @@ export const generalSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.transactionNameGroups.label', { defaultMessage: 'Transaction name groups', }), - description: i18n.translate( - 'xpack.apm.agentConfig.transactionNameGroups.description', - { - defaultMessage: - 'With this option,\n' + - 'you can group transaction names that contain dynamic parts with a wildcard expression.\n' + - 'For example,\n' + - 'the pattern `GET /user/*/cart` would consolidate transactions,\n' + - 'such as `GET /users/42/cart` and `GET /users/73/cart` into a single transaction name `GET /users/*/cart`,\n' + - 'hence reducing the transaction name cardinality.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.transactionNameGroups.description', { + defaultMessage: + 'With this option,\n' + + 'you can group transaction names that contain dynamic parts with a wildcard expression.\n' + + 'For example,\n' + + 'the pattern `GET /user/*/cart` would consolidate transactions,\n' + + 'such as `GET /users/42/cart` and `GET /users/73/cart` into a single transaction name `GET /users/*/cart`,\n' + + 'hence reducing the transaction name cardinality.', + }), includeAgents: ['java'], }, ]; diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/index.test.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/index.test.ts index 7964332df8bf8..8f64a88a5dc0e 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/index.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/index.test.ts @@ -182,25 +182,16 @@ describe('filterByAgent', () => { }); it('android/java', () => { - expect(getSettingKeysForAgent('android/java')).toEqual([ - 'recording', - 'session_sample_rate', - ]); + expect(getSettingKeysForAgent('android/java')).toEqual(['recording', 'session_sample_rate']); }); it('iOS/swift', () => { - expect(getSettingKeysForAgent('iOS/swift')).toEqual([ - 'recording', - 'session_sample_rate', - ]); + expect(getSettingKeysForAgent('iOS/swift')).toEqual(['recording', 'session_sample_rate']); }); it('"All" services (no agent name)', () => { expect(getSettingKeysForAgent(undefined)).toEqual( - expect.arrayContaining([ - 'transaction_max_spans', - 'transaction_sample_rate', - ]) + expect.arrayContaining(['transaction_max_spans', 'transaction_sample_rate']) ); }); }); diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/index.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/index.ts index dbe1da9988851..200ce169a05f1 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/index.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/index.ts @@ -132,8 +132,6 @@ export function validateSetting(setting: SettingDefinition, value: unknown) { } export const settingDefinitions: SettingDefinition[] = sortBy( - [...generalSettings, ...javaSettings, ...mobileSettings].map( - getSettingDefaults - ), + [...generalSettings, ...javaSettings, ...mobileSettings].map(getSettingDefaults), 'key' ); diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/java_settings.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/java_settings.ts index 45abfb43edb00..5d30e208d9202 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/java_settings.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/java_settings.ts @@ -16,18 +16,15 @@ export const javaSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.applicationPackages.label', { defaultMessage: 'Application packages', }), - description: i18n.translate( - 'xpack.apm.agentConfig.applicationPackages.description', - { - defaultMessage: - 'Used to determine whether a stack trace frame is an in-app frame or a library frame. ' + - 'This allows the APM app to collapse the stack frames of library code, and highlight the stack frames that originate from your application. ' + - 'Multiple root packages can be set as a comma-separated list; there’s no need to configure sub-packages. ' + - 'Because this setting helps determine which classes to scan on startup, setting this option can also improve startup time.\n' + - '\n' + - 'You must set this option in order to use the API annotations `@CaptureTransaction` and `@CaptureSpan`.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.applicationPackages.description', { + defaultMessage: + 'Used to determine whether a stack trace frame is an in-app frame or a library frame. ' + + 'This allows the APM app to collapse the stack frames of library code, and highlight the stack frames that originate from your application. ' + + 'Multiple root packages can be set as a comma-separated list; there’s no need to configure sub-packages. ' + + 'Because this setting helps determine which classes to scan on startup, setting this option can also improve startup time.\n' + + '\n' + + 'You must set this option in order to use the API annotations `@CaptureTransaction` and `@CaptureSpan`.', + }), includeAgents: ['java'], }, @@ -39,13 +36,10 @@ export const javaSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.enableLogCorrelation.label', { defaultMessage: 'Enable log correlation', }), - description: i18n.translate( - 'xpack.apm.agentConfig.enableLogCorrelation.description', - { - defaultMessage: - "A boolean specifying if the agent should integrate into SLF4J's MDC to enable trace-log correlation. If set to `true`, the agent will set the `trace.id` and `transaction.id` for the currently active spans and transactions to the MDC. Since Java agent version 1.16.0, the agent also adds `error.id` of captured error to the MDC just before the error message is logged. NOTE: While it's allowed to enable this setting at runtime, you can't disable it without a restart.", - } - ), + description: i18n.translate('xpack.apm.agentConfig.enableLogCorrelation.description', { + defaultMessage: + "A boolean specifying if the agent should integrate into SLF4J's MDC to enable trace-log correlation. If set to `true`, the agent will set the `trace.id` and `transaction.id` for the currently active spans and transactions to the MDC. Since Java agent version 1.16.0, the agent also adds `error.id` of captured error to the MDC just before the error message is logged. NOTE: While it's allowed to enable this setting at runtime, you can't disable it without a restart.", + }), includeAgents: ['java'], }, @@ -60,13 +54,10 @@ export const javaSettings: RawSettingDefinition[] = [ type: 'boolean', category: 'Circuit-Breaker', defaultValue: 'false', - description: i18n.translate( - 'xpack.apm.agentConfig.circuitBreakerEnabled.description', - { - defaultMessage: - 'A boolean specifying whether the circuit breaker should be enabled or not. When enabled, the agent periodically polls stress monitors to detect system/process/JVM stress state. If ANY of the monitors detects a stress indication, the agent will pause, as if the `recording` configuration option has been set to `false`, thus reducing resource consumption to a minimum. When paused, the agent continues polling the same monitors in order to detect whether the stress state has been relieved. If ALL monitors approve that the system/process/JVM is not under stress anymore, the agent will resume and become fully functional.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.circuitBreakerEnabled.description', { + defaultMessage: + 'A boolean specifying whether the circuit breaker should be enabled or not. When enabled, the agent periodically polls stress monitors to detect system/process/JVM stress state. If ANY of the monitors detects a stress indication, the agent will pause, as if the `recording` configuration option has been set to `false`, thus reducing resource consumption to a minimum. When paused, the agent continues polling the same monitors in order to detect whether the stress state has been relieved. If ALL monitors approve that the system/process/JVM is not under stress anymore, the agent will resume and become fully functional.', + }), includeAgents: ['java'], }, @@ -74,12 +65,9 @@ export const javaSettings: RawSettingDefinition[] = [ key: 'enable_experimental_instrumentations', type: 'boolean', defaultValue: 'false', - label: i18n.translate( - 'xpack.apm.agentConfig.enableExperimentalInstrumentations.label', - { - defaultMessage: 'Enable experimental instrumentations', - } - ), + label: i18n.translate('xpack.apm.agentConfig.enableExperimentalInstrumentations.label', { + defaultMessage: 'Enable experimental instrumentations', + }), description: i18n.translate( 'xpack.apm.agentConfig.enableExperimentalInstrumentations.description', { @@ -97,26 +85,20 @@ export const javaSettings: RawSettingDefinition[] = [ key: 'enable_instrumentations', type: 'text', defaultValue: '', - label: i18n.translate( - 'xpack.apm.agentConfig.enableInstrumentations.label', - { - defaultMessage: 'Disable instrumentations', - } - ), - description: i18n.translate( - 'xpack.apm.agentConfig.enableInstrumentations.description', - { - defaultMessage: - 'A list of instrumentations which should be selectively enabled. ' + - 'Valid options are listed in the ' + - '[Java APM Agent documentation](https://www.elastic.co/guide/en/apm/agent/java/current/config-core.html#config-disable-instrumentations).\n' + - '\n' + - 'When set to non-empty value, only listed instrumentations will be enabled ' + - 'if they are not disabled through `disable_instrumentations`or `enable_experimental_instrumentations`.\n' + - 'When not set or empty (default), all instrumentations enabled by default will be enabled ' + - 'unless they are disabled through `disable_instrumentations` or `enable_experimental_instrumentations`.', - } - ), + label: i18n.translate('xpack.apm.agentConfig.enableInstrumentations.label', { + defaultMessage: 'Disable instrumentations', + }), + description: i18n.translate('xpack.apm.agentConfig.enableInstrumentations.description', { + defaultMessage: + 'A list of instrumentations which should be selectively enabled. ' + + 'Valid options are listed in the ' + + '[Java APM Agent documentation](https://www.elastic.co/guide/en/apm/agent/java/current/config-core.html#config-disable-instrumentations).\n' + + '\n' + + 'When set to non-empty value, only listed instrumentations will be enabled ' + + 'if they are not disabled through `disable_instrumentations`or `enable_experimental_instrumentations`.\n' + + 'When not set or empty (default), all instrumentations enabled by default will be enabled ' + + 'unless they are disabled through `disable_instrumentations` or `enable_experimental_instrumentations`.', + }), includeAgents: ['java'], }, @@ -127,16 +109,13 @@ export const javaSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.logSending.label', { defaultMessage: 'Log sending (experimental)', }), - description: i18n.translate( - 'xpack.apm.agentConfig.logSending.description', - { - defaultMessage: - 'Experimental, requires latest version of the Java agent.\n' + - '\n' + - 'If set to `true`,\n' + - 'agent will send logs directly to APM server.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.logSending.description', { + defaultMessage: + 'Experimental, requires latest version of the Java agent.\n' + + '\n' + + 'If set to `true`,\n' + + 'agent will send logs directly to APM server.', + }), includeAgents: ['java'], }, @@ -148,28 +127,24 @@ export const javaSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.spanMinDuration.label', { defaultMessage: 'Span minimum duration', }), - description: i18n.translate( - 'xpack.apm.agentConfig.spanMinDuration.description', - { - defaultMessage: - 'Sets the minimum duration of spans. Spans that execute faster than this threshold are attempted to be discarded.\n' + - '\n' + - 'The attempt fails if they lead up to a span that can’t be discarded. Spans that propagate the trace context to ' + - 'downstream services, such as outgoing HTTP requests, can’t be discarded. Additionally, spans that lead to an error ' + - 'or that may be a parent of an async operation can’t be discarded.\n' + - '\n' + - 'However, external calls that don’t propagate context, such as calls to a database, can be discarded using this threshold.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.spanMinDuration.description', { + defaultMessage: + 'Sets the minimum duration of spans. Spans that execute faster than this threshold are attempted to be discarded.\n' + + '\n' + + 'The attempt fails if they lead up to a span that can’t be discarded. Spans that propagate the trace context to ' + + 'downstream services, such as outgoing HTTP requests, can’t be discarded. Additionally, spans that lead to an error ' + + 'or that may be a parent of an async operation can’t be discarded.\n' + + '\n' + + 'However, external calls that don’t propagate context, such as calls to a database, can be discarded using this threshold.', + }), includeAgents: ['java'], }, { key: 'stress_monitor_gc_stress_threshold', - label: i18n.translate( - 'xpack.apm.agentConfig.stressMonitorGcStressThreshold.label', - { defaultMessage: 'Stress monitor gc stress threshold' } - ), + label: i18n.translate('xpack.apm.agentConfig.stressMonitorGcStressThreshold.label', { + defaultMessage: 'Stress monitor gc stress threshold', + }), type: 'float', category: 'Circuit-Breaker', defaultValue: '0.95', @@ -184,10 +159,9 @@ export const javaSettings: RawSettingDefinition[] = [ }, { key: 'stress_monitor_gc_relief_threshold', - label: i18n.translate( - 'xpack.apm.agentConfig.stressMonitorGcReliefThreshold.label', - { defaultMessage: 'Stress monitor gc relief threshold' } - ), + label: i18n.translate('xpack.apm.agentConfig.stressMonitorGcReliefThreshold.label', { + defaultMessage: 'Stress monitor gc relief threshold', + }), type: 'float', category: 'Circuit-Breaker', @@ -203,10 +177,9 @@ export const javaSettings: RawSettingDefinition[] = [ }, { key: 'stress_monitor_cpu_duration_threshold', - label: i18n.translate( - 'xpack.apm.agentConfig.stressMonitorCpuDurationThreshold.label', - { defaultMessage: 'Stress monitor cpu duration threshold' } - ), + label: i18n.translate('xpack.apm.agentConfig.stressMonitorCpuDurationThreshold.label', { + defaultMessage: 'Stress monitor cpu duration threshold', + }), type: 'duration', category: 'Circuit-Breaker', defaultValue: '1m', @@ -222,10 +195,9 @@ export const javaSettings: RawSettingDefinition[] = [ }, { key: 'stress_monitor_system_cpu_stress_threshold', - label: i18n.translate( - 'xpack.apm.agentConfig.stressMonitorSystemCpuStressThreshold.label', - { defaultMessage: 'Stress monitor system cpu stress threshold' } - ), + label: i18n.translate('xpack.apm.agentConfig.stressMonitorSystemCpuStressThreshold.label', { + defaultMessage: 'Stress monitor system cpu stress threshold', + }), type: 'float', category: 'Circuit-Breaker', defaultValue: '0.95', @@ -240,10 +212,9 @@ export const javaSettings: RawSettingDefinition[] = [ }, { key: 'stress_monitor_system_cpu_relief_threshold', - label: i18n.translate( - 'xpack.apm.agentConfig.stressMonitorSystemCpuReliefThreshold.label', - { defaultMessage: 'Stress monitor system cpu relief threshold' } - ), + label: i18n.translate('xpack.apm.agentConfig.stressMonitorSystemCpuReliefThreshold.label', { + defaultMessage: 'Stress monitor system cpu relief threshold', + }), type: 'float', category: 'Circuit-Breaker', defaultValue: '0.8', @@ -263,28 +234,23 @@ export const javaSettings: RawSettingDefinition[] = [ { key: 'profiling_inferred_spans_enabled', - label: i18n.translate( - 'xpack.apm.agentConfig.profilingInferredSpansEnabled.label', - { defaultMessage: 'Profiling inferred spans enabled' } - ), + label: i18n.translate('xpack.apm.agentConfig.profilingInferredSpansEnabled.label', { + defaultMessage: 'Profiling inferred spans enabled', + }), type: 'boolean', category: 'Profiling', defaultValue: 'false', - description: i18n.translate( - 'xpack.apm.agentConfig.profilingInferredSpansEnabled.description', - { - defaultMessage: - 'Set to `true` to make the agent create spans for method executions based on async-profiler, a sampling aka statistical profiler. Due to the nature of how sampling profilers work, the duration of the inferred spans are not exact, but only estimations. The `profiling_inferred_spans_sampling_interval` lets you fine tune the trade-off between accuracy and overhead. The inferred spans are created after a profiling session has ended. This means there is a delay between the regular and the inferred spans being visible in the UI. NOTE: This feature is not available on Windows.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.profilingInferredSpansEnabled.description', { + defaultMessage: + 'Set to `true` to make the agent create spans for method executions based on async-profiler, a sampling aka statistical profiler. Due to the nature of how sampling profilers work, the duration of the inferred spans are not exact, but only estimations. The `profiling_inferred_spans_sampling_interval` lets you fine tune the trade-off between accuracy and overhead. The inferred spans are created after a profiling session has ended. This means there is a delay between the regular and the inferred spans being visible in the UI. NOTE: This feature is not available on Windows.', + }), includeAgents: ['java'], }, { key: 'profiling_inferred_spans_sampling_interval', - label: i18n.translate( - 'xpack.apm.agentConfig.profilingInferredSpansSamplingInterval.label', - { defaultMessage: 'Profiling inferred spans sampling interval' } - ), + label: i18n.translate('xpack.apm.agentConfig.profilingInferredSpansSamplingInterval.label', { + defaultMessage: 'Profiling inferred spans sampling interval', + }), type: 'duration', category: 'Profiling', defaultValue: '50ms', @@ -301,10 +267,9 @@ export const javaSettings: RawSettingDefinition[] = [ }, { key: 'profiling_inferred_spans_min_duration', - label: i18n.translate( - 'xpack.apm.agentConfig.profilingInferredSpansMinDuration.label', - { defaultMessage: 'Profiling inferred spans min duration' } - ), + label: i18n.translate('xpack.apm.agentConfig.profilingInferredSpansMinDuration.label', { + defaultMessage: 'Profiling inferred spans min duration', + }), type: 'duration', category: 'Profiling', defaultValue: '0ms', @@ -320,10 +285,9 @@ export const javaSettings: RawSettingDefinition[] = [ }, { key: 'profiling_inferred_spans_included_classes', - label: i18n.translate( - 'xpack.apm.agentConfig.profilingInferredSpansIncludedClasses.label', - { defaultMessage: 'Profiling inferred spans included classes' } - ), + label: i18n.translate('xpack.apm.agentConfig.profilingInferredSpansIncludedClasses.label', { + defaultMessage: 'Profiling inferred spans included classes', + }), type: 'text', category: 'Profiling', defaultValue: '*', @@ -338,10 +302,9 @@ export const javaSettings: RawSettingDefinition[] = [ }, { key: 'profiling_inferred_spans_excluded_classes', - label: i18n.translate( - 'xpack.apm.agentConfig.profilingInferredSpansExcludedClasses.label', - { defaultMessage: 'Profiling inferred spans excluded classes' } - ), + label: i18n.translate('xpack.apm.agentConfig.profilingInferredSpansExcludedClasses.label', { + defaultMessage: 'Profiling inferred spans excluded classes', + }), type: 'text', category: 'Profiling', defaultValue: @@ -363,19 +326,16 @@ export const javaSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.captureJmxMetrics.label', { defaultMessage: 'Capture JMX metrics', }), - description: i18n.translate( - 'xpack.apm.agentConfig.captureJmxMetrics.description', - { - defaultMessage: - 'Report metrics from JMX to the APM Server\n' + - '\n' + - 'Can contain multiple comma separated JMX metric definitions:\n' + - '\n' + - '`object_name[] attribute[:metric_name=]`\n' + - '\n' + - 'See the Java agent documentation for more details.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.captureJmxMetrics.description', { + defaultMessage: + 'Report metrics from JMX to the APM Server\n' + + '\n' + + 'Can contain multiple comma separated JMX metric definitions:\n' + + '\n' + + '`object_name[] attribute[:metric_name=]`\n' + + '\n' + + 'See the Java agent documentation for more details.', + }), includeAgents: ['java'], }, @@ -386,14 +346,11 @@ export const javaSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.ignoreExceptions.label', { defaultMessage: 'Ignore exceptions', }), - description: i18n.translate( - 'xpack.apm.agentConfig.ignoreExceptions.description', - { - defaultMessage: - 'A list of exceptions that should be ignored and not reported as errors.\n' + - 'This allows to ignore exceptions thrown in regular control flow that are not actual errors.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.ignoreExceptions.description', { + defaultMessage: + 'A list of exceptions that should be ignored and not reported as errors.\n' + + 'This allows to ignore exceptions thrown in regular control flow that are not actual errors.', + }), includeAgents: ['java'], }, @@ -404,26 +361,23 @@ export const javaSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.traceMethods.label', { defaultMessage: 'Trace methods', }), - description: i18n.translate( - 'xpack.apm.agentConfig.traceMethods.description', - { - defaultMessage: - 'A list of methods for which to create a transaction or span.\n' + - '\n' + - 'If you want to monitor a large number of methods,\n' + - 'use `profiling_inferred_spans_enabled`.\n' + - '\n' + - 'This works by instrumenting each matching method to include code that creates a span for the method.\n' + - 'While creating a span is quite cheap in terms of performance,\n' + - 'instrumenting a whole code base or a method which is executed in a tight loop leads to significant overhead.\n' + - '\n' + - 'NOTE: Only use wildcards if necessary.\n' + - 'The more methods you match the more overhead will be caused by the agent.\n' + - 'Also note that there is a maximum amount of spans per transaction `transaction_max_spans`.\n' + - '\n' + - 'See the Java agent documentation for more details.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.traceMethods.description', { + defaultMessage: + 'A list of methods for which to create a transaction or span.\n' + + '\n' + + 'If you want to monitor a large number of methods,\n' + + 'use `profiling_inferred_spans_enabled`.\n' + + '\n' + + 'This works by instrumenting each matching method to include code that creates a span for the method.\n' + + 'While creating a span is quite cheap in terms of performance,\n' + + 'instrumenting a whole code base or a method which is executed in a tight loop leads to significant overhead.\n' + + '\n' + + 'NOTE: Only use wildcards if necessary.\n' + + 'The more methods you match the more overhead will be caused by the agent.\n' + + 'Also note that there is a maximum amount of spans per transaction `transaction_max_spans`.\n' + + '\n' + + 'See the Java agent documentation for more details.', + }), includeAgents: ['java'], }, @@ -434,16 +388,13 @@ export const javaSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.unnestExceptions.label', { defaultMessage: 'Unnest exceptions', }), - description: i18n.translate( - 'xpack.apm.agentConfig.unnestExceptions.description', - { - defaultMessage: - 'When reporting exceptions,\n' + - 'un-nests the exceptions matching the wildcard pattern.\n' + - "This can come in handy for Spring's `org.springframework.web.util.NestedServletException`,\n" + - 'for example.', - } - ), + description: i18n.translate('xpack.apm.agentConfig.unnestExceptions.description', { + defaultMessage: + 'When reporting exceptions,\n' + + 'un-nests the exceptions matching the wildcard pattern.\n' + + "This can come in handy for Spring's `org.springframework.web.util.NestedServletException`,\n" + + 'for example.', + }), includeAgents: ['java'], }, ]; diff --git a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/mobile_settings.ts b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/mobile_settings.ts index c334c3a72aaa3..b171614f7fb2a 100644 --- a/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/mobile_settings.ts +++ b/x-pack/plugins/observability_solution/apm/common/agent_configuration/setting_definitions/mobile_settings.ts @@ -17,13 +17,10 @@ export const mobileSettings: RawSettingDefinition[] = [ label: i18n.translate('xpack.apm.agentConfig.sessionSampleRate.label', { defaultMessage: 'Session sample rate', }), - description: i18n.translate( - 'xpack.apm.agentConfig.sessionSampleRate.description', - { - defaultMessage: - "By default, the agent will sample all signals generated by your application (e.g. spans, metrics, & logs). To reduce overhead and storage requirements, you can set the sample rate to a value between 0.0 and 1.0. When reduced below 1.0, data will be sampled per session. This is so context in a given session isn't lost.", - } - ), + description: i18n.translate('xpack.apm.agentConfig.sessionSampleRate.description', { + defaultMessage: + "By default, the agent will sample all signals generated by your application (e.g. spans, metrics, & logs). To reduce overhead and storage requirements, you can set the sample rate to a value between 0.0 and 1.0. When reduced below 1.0, data will be sampled per session. This is so context in a given session isn't lost.", + }), includeAgents: ['iOS/swift', 'android/java'], }, ]; diff --git a/x-pack/plugins/observability_solution/apm/common/anomaly_detection/get_preferred_service_anomaly_timeseries.ts b/x-pack/plugins/observability_solution/apm/common/anomaly_detection/get_preferred_service_anomaly_timeseries.ts index 04f1c72a3c334..0f1ff8c4f30a6 100644 --- a/x-pack/plugins/observability_solution/apm/common/anomaly_detection/get_preferred_service_anomaly_timeseries.ts +++ b/x-pack/plugins/observability_solution/apm/common/anomaly_detection/get_preferred_service_anomaly_timeseries.ts @@ -20,9 +20,7 @@ export function getPreferredServiceAnomalyTimeseries({ allAnomalyTimeseries: ServiceAnomalyTimeseries[]; fallbackToTransactions: boolean; }) { - const seriesForType = allAnomalyTimeseries.filter( - (serie) => serie.type === detectorType - ); + const seriesForType = allAnomalyTimeseries.filter((serie) => serie.type === detectorType); return seriesForType.find( (serie) => diff --git a/x-pack/plugins/observability_solution/apm/common/anomaly_detection/index.ts b/x-pack/plugins/observability_solution/apm/common/anomaly_detection/index.ts index 23912461412e2..68b6aa72fca58 100644 --- a/x-pack/plugins/observability_solution/apm/common/anomaly_detection/index.ts +++ b/x-pack/plugins/observability_solution/apm/common/anomaly_detection/index.ts @@ -32,12 +32,9 @@ export function getSeverityColor(score: number) { } export const ML_ERRORS = { - INVALID_LICENSE: i18n.translate( - 'xpack.apm.anomaly_detection.error.invalid_license', - { - defaultMessage: `To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning.`, - } - ), + INVALID_LICENSE: i18n.translate('xpack.apm.anomaly_detection.error.invalid_license', { + defaultMessage: `To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning.`, + }), MISSING_READ_PRIVILEGES: i18n.translate( 'xpack.apm.anomaly_detection.error.missing_read_privileges', { @@ -52,12 +49,9 @@ export const ML_ERRORS = { 'You must have "write" privileges to Machine Learning and APM in order to create Anomaly Detection jobs', } ), - ML_NOT_AVAILABLE: i18n.translate( - 'xpack.apm.anomaly_detection.error.not_available', - { - defaultMessage: 'Machine learning is not available', - } - ), + ML_NOT_AVAILABLE: i18n.translate('xpack.apm.anomaly_detection.error.not_available', { + defaultMessage: 'Machine learning is not available', + }), ML_NOT_AVAILABLE_IN_SPACE: i18n.translate( 'xpack.apm.anomaly_detection.error.not_available_in_space', { diff --git a/x-pack/plugins/observability_solution/apm/common/apm_api/parse_endpoint.ts b/x-pack/plugins/observability_solution/apm/common/apm_api/parse_endpoint.ts index fb7ef6d36ce25..bd8eac7387dd3 100644 --- a/x-pack/plugins/observability_solution/apm/common/apm_api/parse_endpoint.ts +++ b/x-pack/plugins/observability_solution/apm/common/apm_api/parse_endpoint.ts @@ -7,10 +7,7 @@ type Method = 'get' | 'post' | 'put' | 'delete'; -export function parseEndpoint( - endpoint: string, - pathParams: Record = {} -) { +export function parseEndpoint(endpoint: string, pathParams: Record = {}) { const [method, rawPathname] = endpoint.split(' '); // replace template variables with path params diff --git a/x-pack/plugins/observability_solution/apm/common/apm_feature_flags.ts b/x-pack/plugins/observability_solution/apm/common/apm_feature_flags.ts index 3b9da8cdebd92..8f860a9c6ce86 100644 --- a/x-pack/plugins/observability_solution/apm/common/apm_feature_flags.ts +++ b/x-pack/plugins/observability_solution/apm/common/apm_feature_flags.ts @@ -65,9 +65,9 @@ export type ApmFeatureFlags = { [TApmFeatureFlagName in keyof ApmFeatureFlagMap]: ValueOfApmFeatureFlag; }; -export type ValueOfApmFeatureFlag< - TApmFeatureFlagName extends ApmFeatureFlagName -> = t.OutputOf; +export type ValueOfApmFeatureFlag = t.OutputOf< + ApmFeatureFlagMap[TApmFeatureFlagName]['type'] +>; export function getApmFeatureFlags(): ApmFeatureFlags { return mapValues(apmFeatureFlagMap, (value, key) => value.default); diff --git a/x-pack/plugins/observability_solution/apm/common/apm_telemetry.test.ts b/x-pack/plugins/observability_solution/apm/common/apm_telemetry.test.ts index 6fd7606a3031e..2181e0df3375e 100644 --- a/x-pack/plugins/observability_solution/apm/common/apm_telemetry.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/apm_telemetry.test.ts @@ -5,10 +5,7 @@ * 2.0. */ -import { - getApmTelemetryMapping, - mergeApmTelemetryMapping, -} from './apm_telemetry'; +import { getApmTelemetryMapping, mergeApmTelemetryMapping } from './apm_telemetry'; // Add this snapshot serializer for this test. The default snapshot serializer // prints "Object" next to objects in the JSON output, but we want to be able to @@ -80,8 +77,8 @@ describe('APM telemetry helpers', () => { }; expect( - mergeApmTelemetryMapping(validTelemetryMapping)?.mappings.properties - .stack_stats.properties.kibana.properties.plugins.properties.apm + mergeApmTelemetryMapping(validTelemetryMapping)?.mappings.properties.stack_stats + .properties.kibana.properties.plugins.properties.apm ).toEqual(getApmTelemetryMapping()); }); }); diff --git a/x-pack/plugins/observability_solution/apm/common/apm_telemetry.ts b/x-pack/plugins/observability_solution/apm/common/apm_telemetry.ts index d3b318e62eab3..634ced4702740 100644 --- a/x-pack/plugins/observability_solution/apm/common/apm_telemetry.ts +++ b/x-pack/plugins/observability_solution/apm/common/apm_telemetry.ts @@ -21,10 +21,7 @@ function schemaToMapping(schemaLeaf: any): any { return Object.entries(schemaLeaf).reduce((acc, [key, value]) => { const propMapping = schemaToMapping(value); - acc[key] = - typeof propMapping.type === 'string' - ? propMapping - : { properties: propMapping }; + acc[key] = typeof propMapping.type === 'string' ? propMapping : { properties: propMapping }; return acc; }, {} as Record); @@ -43,9 +40,7 @@ export function getApmTelemetryMapping() { * Merge a telemetry mapping object (from https://github.com/elastic/telemetry/blob/master/config/templates/xpack-phone-home.json) * with the output from `getApmTelemetryMapping`. */ -export function mergeApmTelemetryMapping( - xpackPhoneHomeMapping: Record -) { +export function mergeApmTelemetryMapping(xpackPhoneHomeMapping: Record) { return produce(xpackPhoneHomeMapping, (draft: Record) => { draft.mappings.properties.stack_stats.properties.kibana.properties.plugins.properties.apm = getApmTelemetryMapping(); diff --git a/x-pack/plugins/observability_solution/apm/common/connections.ts b/x-pack/plugins/observability_solution/apm/common/connections.ts index 6d87e3517e8c3..10a10e96e5190 100644 --- a/x-pack/plugins/observability_solution/apm/common/connections.ts +++ b/x-pack/plugins/observability_solution/apm/common/connections.ts @@ -70,7 +70,5 @@ export interface ConnectionStatsItemWithComparisonData { } export function getNodeName(node: Node) { - return node.type === NodeType.service - ? node.serviceName - : node.dependencyName; + return node.type === NodeType.service ? node.serviceName : node.dependencyName; } diff --git a/x-pack/plugins/observability_solution/apm/common/correlations/constants.ts b/x-pack/plugins/observability_solution/apm/common/correlations/constants.ts index 115e50582823a..f42c5b1c4a81b 100644 --- a/x-pack/plugins/observability_solution/apm/common/correlations/constants.ts +++ b/x-pack/plugins/observability_solution/apm/common/correlations/constants.ts @@ -62,11 +62,7 @@ export const FIELDS_TO_ADD_AS_CANDIDATE = new Set([ 'process.args', 'http.response.status_code', ]); -export const FIELD_PREFIX_TO_ADD_AS_CANDIDATE = [ - 'cloud.', - 'labels.', - 'user_agent.', -]; +export const FIELD_PREFIX_TO_ADD_AS_CANDIDATE = ['cloud.', 'labels.', 'user_agent.']; /** * Other constants diff --git a/x-pack/plugins/observability_solution/apm/common/correlations/utils/get_prioritized_field_value_pairs.test.ts b/x-pack/plugins/observability_solution/apm/common/correlations/utils/get_prioritized_field_value_pairs.test.ts index 227173c317780..1dc3aece117f5 100644 --- a/x-pack/plugins/observability_solution/apm/common/correlations/utils/get_prioritized_field_value_pairs.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/correlations/utils/get_prioritized_field_value_pairs.test.ts @@ -14,8 +14,7 @@ describe('correlations', () => { { fieldName: 'the-field-1', fieldValue: 'the-value-1' }, { fieldName: 'the-field-2', fieldValue: 'the-value-2' }, ]; - const prioritziedFieldValuePairs = - getPrioritizedFieldValuePairs(fieldValuePairs); + const prioritziedFieldValuePairs = getPrioritizedFieldValuePairs(fieldValuePairs); expect(prioritziedFieldValuePairs.map((d) => d.fieldName)).toEqual([ 'the-field-1', 'the-field-2', @@ -27,8 +26,7 @@ describe('correlations', () => { { fieldName: 'service.version', fieldValue: 'the-value-1' }, { fieldName: 'the-field-2', fieldValue: 'the-value-2' }, ]; - const prioritziedFieldValuePairs = - getPrioritizedFieldValuePairs(fieldValuePairs); + const prioritziedFieldValuePairs = getPrioritizedFieldValuePairs(fieldValuePairs); expect(prioritziedFieldValuePairs.map((d) => d.fieldName)).toEqual([ 'service.version', 'the-field-2', @@ -40,8 +38,7 @@ describe('correlations', () => { { fieldName: 'the-field-1', fieldValue: 'the-value-1' }, { fieldName: 'service.version', fieldValue: 'the-value-2' }, ]; - const prioritziedFieldValuePairs = - getPrioritizedFieldValuePairs(fieldValuePairs); + const prioritziedFieldValuePairs = getPrioritizedFieldValuePairs(fieldValuePairs); expect(prioritziedFieldValuePairs.map((d) => d.fieldName)).toEqual([ 'service.version', 'the-field-1', @@ -54,8 +51,7 @@ describe('correlations', () => { { fieldName: 'service.version', fieldValue: 'the-value-2' }, { fieldName: 'cloud.the-field-3', fieldValue: 'the-value-3' }, ]; - const prioritziedFieldValuePairs = - getPrioritizedFieldValuePairs(fieldValuePairs); + const prioritziedFieldValuePairs = getPrioritizedFieldValuePairs(fieldValuePairs); expect(prioritziedFieldValuePairs.map((d) => d.fieldName)).toEqual([ 'service.version', 'cloud.the-field-3', diff --git a/x-pack/plugins/observability_solution/apm/common/correlations/utils/get_prioritized_field_value_pairs.ts b/x-pack/plugins/observability_solution/apm/common/correlations/utils/get_prioritized_field_value_pairs.ts index 4a0086ba02a6d..36cf68e7f0f2e 100644 --- a/x-pack/plugins/observability_solution/apm/common/correlations/utils/get_prioritized_field_value_pairs.ts +++ b/x-pack/plugins/observability_solution/apm/common/correlations/utils/get_prioritized_field_value_pairs.ts @@ -10,9 +10,7 @@ import { hasPrefixToInclude } from './has_prefix_to_include'; import type { FieldValuePair } from '../types'; -export const getPrioritizedFieldValuePairs = ( - fieldValuePairs: FieldValuePair[] -) => { +export const getPrioritizedFieldValuePairs = (fieldValuePairs: FieldValuePair[]) => { const prioritizedFields = [...FIELDS_TO_ADD_AS_CANDIDATE]; return fieldValuePairs.sort((a, b) => { diff --git a/x-pack/plugins/observability_solution/apm/common/correlations/utils/has_prefix_to_include.test.ts b/x-pack/plugins/observability_solution/apm/common/correlations/utils/has_prefix_to_include.test.ts index a951dc63caad9..e8b1a82450d5d 100644 --- a/x-pack/plugins/observability_solution/apm/common/correlations/utils/has_prefix_to_include.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/correlations/utils/has_prefix_to_include.test.ts @@ -18,9 +18,7 @@ describe('aggregation utils', () => { }); it('returns false if the prefix is included', async () => { FIELD_PREFIX_TO_ADD_AS_CANDIDATE.forEach((prefix) => { - expect( - hasPrefixToInclude(`unknown-prefix-.${prefix}the-field-name`) - ).toBe(false); + expect(hasPrefixToInclude(`unknown-prefix-.${prefix}the-field-name`)).toBe(false); expect(hasPrefixToInclude('the-field-name')).toBe(false); }); }); diff --git a/x-pack/plugins/observability_solution/apm/common/correlations/utils/has_prefix_to_include.ts b/x-pack/plugins/observability_solution/apm/common/correlations/utils/has_prefix_to_include.ts index baf4d62af00fa..524aa8a73790a 100644 --- a/x-pack/plugins/observability_solution/apm/common/correlations/utils/has_prefix_to_include.ts +++ b/x-pack/plugins/observability_solution/apm/common/correlations/utils/has_prefix_to_include.ts @@ -8,7 +8,5 @@ import { FIELD_PREFIX_TO_ADD_AS_CANDIDATE } from '../constants'; export const hasPrefixToInclude = (fieldName: string) => { - return FIELD_PREFIX_TO_ADD_AS_CANDIDATE.some((prefix) => - fieldName.startsWith(prefix) - ); + return FIELD_PREFIX_TO_ADD_AS_CANDIDATE.some((prefix) => fieldName.startsWith(prefix)); }; diff --git a/x-pack/plugins/observability_solution/apm/common/critical_path/get_aggregated_critical_path_root_nodes.ts b/x-pack/plugins/observability_solution/apm/common/critical_path/get_aggregated_critical_path_root_nodes.ts index 5d00db3977b07..f6b6763fe5c1b 100644 --- a/x-pack/plugins/observability_solution/apm/common/critical_path/get_aggregated_critical_path_root_nodes.ts +++ b/x-pack/plugins/observability_solution/apm/common/critical_path/get_aggregated_critical_path_root_nodes.ts @@ -28,29 +28,24 @@ export function getAggregatedCriticalPathRootNodes(params: { let numNodes = 0; - function mergeNodesWithSameOperationId( - nodes: CriticalPathTreeNode[] - ): CriticalPathTreeNode[] { + function mergeNodesWithSameOperationId(nodes: CriticalPathTreeNode[]): CriticalPathTreeNode[] { const nodesByOperationId: Record = {}; - const mergedNodes = nodes.reduce( - (prev, node, index, array) => { - const nodeId = node.nodeId; - const operationId = criticalPath.operationIdByNodeId[nodeId]; - if (nodesByOperationId[operationId]) { - const prevNode = nodesByOperationId[operationId]; - prevNode.children.push(...node.children); - prevNode.countExclusive += node.countExclusive; - prevNode.countInclusive += node.countInclusive; - return prev; - } - - nodesByOperationId[operationId] = node; - - prev.push(node); + const mergedNodes = nodes.reduce((prev, node, index, array) => { + const nodeId = node.nodeId; + const operationId = criticalPath.operationIdByNodeId[nodeId]; + if (nodesByOperationId[operationId]) { + const prevNode = nodesByOperationId[operationId]; + prevNode.children.push(...node.children); + prevNode.countExclusive += node.countExclusive; + prevNode.countInclusive += node.countInclusive; return prev; - }, - [] - ); + } + + nodesByOperationId[operationId] = node; + + prev.push(node); + return prev; + }, []); numNodes += mergedNodes.length; diff --git a/x-pack/plugins/observability_solution/apm/common/critical_path/get_critical_path.test.ts b/x-pack/plugins/observability_solution/apm/common/critical_path/get_critical_path.test.ts index 8b78798d2bc55..61720dba3d472 100644 --- a/x-pack/plugins/observability_solution/apm/common/critical_path/get_critical_path.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/critical_path/get_critical_path.test.ts @@ -17,9 +17,7 @@ describe('getCriticalPath', () => { const entryTransaction = dedot(events[0]!, {}) as Transaction; const waterfall = getWaterfall({ traceItems: { - traceDocs: events.map( - (event) => dedot(event, {}) as Transaction | Span - ), + traceDocs: events.map((event) => dedot(event, {}) as Transaction | Span), errorDocs: [], exceedsMax: false, spanLinksCountById: {}, @@ -45,9 +43,7 @@ describe('getCriticalPath', () => { .transaction('/service-a') .timestamp(1) .duration(100) - .children( - service.span('foo', 'external', 'db').duration(100).timestamp(1) - ) + .children(service.span('foo', 'external', 'db').duration(100).timestamp(1)) .serialize() ); @@ -76,9 +72,7 @@ describe('getCriticalPath', () => { .serialize() ); - const longerSpan = waterfall.items.find( - (item) => (item.doc as Span).span?.name === 'bar' - ); + const longerSpan = waterfall.items.find((item) => (item.doc as Span).span?.name === 'bar'); expect(segments).toEqual([ { self: false, duration: 100000, item: waterfall.items[0], offset: 0 }, @@ -103,9 +97,7 @@ describe('getCriticalPath', () => { .transaction('/service-a') .timestamp(1) .duration(100) - .children( - service.span('foo', 'external', 'db').duration(50).timestamp(11) - ) + .children(service.span('foo', 'external', 'db').duration(50).timestamp(11)) .serialize() ); diff --git a/x-pack/plugins/observability_solution/apm/common/critical_path/get_critical_path.ts b/x-pack/plugins/observability_solution/apm/common/critical_path/get_critical_path.ts index ad4e166962ccb..f881362b39078 100644 --- a/x-pack/plugins/observability_solution/apm/common/critical_path/get_critical_path.ts +++ b/x-pack/plugins/observability_solution/apm/common/critical_path/get_critical_path.ts @@ -67,11 +67,7 @@ export function getCriticalPath(waterfall: IWaterfall): CriticalPath { // - The span/tx ends before the start of the initial scan period. // - The span ends _after_ the current scan time. - ( - normalizedChildStart >= scanTime || - normalizedChildEnd < start || - childEnd > scanTime - ) + (normalizedChildStart >= scanTime || normalizedChildEnd < start || childEnd > scanTime) ); if (!isOnCriticalPath) { @@ -124,8 +120,7 @@ export function getCriticalPath(waterfall: IWaterfall): CriticalPath { if (waterfall.entryWaterfallTransaction) { const start = - waterfall.entryWaterfallTransaction.skew + - waterfall.entryWaterfallTransaction.offset; + waterfall.entryWaterfallTransaction.skew + waterfall.entryWaterfallTransaction.offset; scan({ item: waterfall.entryWaterfallTransaction, start, diff --git a/x-pack/plugins/observability_solution/apm/common/custom_link/custom_link.test.ts b/x-pack/plugins/observability_solution/apm/common/custom_link/custom_link.test.ts index 1e2b3b608f74d..c9bebede12afc 100644 --- a/x-pack/plugins/observability_solution/apm/common/custom_link/custom_link.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/custom_link/custom_link.test.ts @@ -28,8 +28,7 @@ describe('Custom link', () => { }); it('handles complex template with multiple variables', () => { - const url = - 'https://example.com/{{name}}/details/{{age}}?param={{gender}}'; + const url = 'https://example.com/{{name}}/details/{{age}}?param={{gender}}'; const result = extractTemplateVariableNames(url); expect(result).toEqual(['name', 'age', 'gender']); }); @@ -99,9 +98,7 @@ describe('Custom link', () => { service: { name: 'foo & bar' }, } as Transaction; const result = getEncodedCustomLinkUrl(url, transaction); - expect(result).toBe( - 'https://kibana.com/app/apm/foo%20%26%20bar/overview' - ); + expect(result).toBe('https://kibana.com/app/apm/foo%20%26%20bar/overview'); }); }); }); diff --git a/x-pack/plugins/observability_solution/apm/common/custom_link/index.ts b/x-pack/plugins/observability_solution/apm/common/custom_link/index.ts index e377d7392b010..cc8d206f9b2b7 100644 --- a/x-pack/plugins/observability_solution/apm/common/custom_link/index.ts +++ b/x-pack/plugins/observability_solution/apm/common/custom_link/index.ts @@ -11,19 +11,15 @@ import { set } from '@kbn/safer-lodash-set'; import Mustache from 'mustache'; import { Transaction } from '../../typings/es_schemas/ui/transaction'; -export const INVALID_LICENSE = i18n.translate( - 'xpack.apm.settings.customLink.license.text', - { - defaultMessage: - "To create custom links, you must be subscribed to an Elastic Gold license or above. With it, you'll have the ability to create custom links to improve your workflow when analyzing your services.", - } -); +export const INVALID_LICENSE = i18n.translate('xpack.apm.settings.customLink.license.text', { + defaultMessage: + "To create custom links, you must be subscribed to an Elastic Gold license or above. With it, you'll have the ability to create custom links to improve your workflow when analyzing your services.", +}); export const NO_PERMISSION_LABEL = i18n.translate( 'xpack.apm.settings.customLink.noPermissionTooltipLabel', { - defaultMessage: - "Your user role doesn't have permissions to create custom links", + defaultMessage: "Your user role doesn't have permissions to create custom links", } ); @@ -35,10 +31,7 @@ export const extractTemplateVariableNames = (url: string): string[] => { return Array.from(uniqueVariableNames); }; -export function getEncodedCustomLinkUrl( - url: string, - transaction?: Transaction -) { +export function getEncodedCustomLinkUrl(url: string, transaction?: Transaction) { try { const templateVariables = extractTemplateVariableNames(url); const encodedTemplateVariables = {}; diff --git a/x-pack/plugins/observability_solution/apm/common/data_source.ts b/x-pack/plugins/observability_solution/apm/common/data_source.ts index 93d8261473692..217862e03e415 100644 --- a/x-pack/plugins/observability_solution/apm/common/data_source.ts +++ b/x-pack/plugins/observability_solution/apm/common/data_source.ts @@ -17,16 +17,13 @@ type AnyApmDocumentType = | ApmDocumentType.ErrorEvent | ApmDocumentType.SpanEvent; -export interface ApmDataSource< - TDocumentType extends AnyApmDocumentType = AnyApmDocumentType -> { +export interface ApmDataSource { rollupInterval: RollupInterval; documentType: TDocumentType; } -export type ApmDataSourceWithSummary< - T extends AnyApmDocumentType = AnyApmDocumentType -> = ApmDataSource & { - hasDurationSummaryField: boolean; - hasDocs: boolean; -}; +export type ApmDataSourceWithSummary = + ApmDataSource & { + hasDurationSummaryField: boolean; + hasDocs: boolean; + }; diff --git a/x-pack/plugins/observability_solution/apm/common/dependencies.ts b/x-pack/plugins/observability_solution/apm/common/dependencies.ts index 812bb33342ba5..174f24a404a2c 100644 --- a/x-pack/plugins/observability_solution/apm/common/dependencies.ts +++ b/x-pack/plugins/observability_solution/apm/common/dependencies.ts @@ -7,10 +7,7 @@ import { i18n } from '@kbn/i18n'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; -import { - PROCESSOR_EVENT, - SPAN_DESTINATION_SERVICE_RESOURCE, -} from './es_fields/apm'; +import { PROCESSOR_EVENT, SPAN_DESTINATION_SERVICE_RESOURCE } from './es_fields/apm'; import { environmentQuery } from './utils/environment_query'; export const unifiedSearchBarPlaceholder = i18n.translate( @@ -30,9 +27,7 @@ export const getSearchBarBoolFilter = ({ return [ { term: { [PROCESSOR_EVENT]: ProcessorEvent.metric } }, { exists: { field: SPAN_DESTINATION_SERVICE_RESOURCE } }, - ...(dependencyName - ? [{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName } }] - : []), + ...(dependencyName ? [{ term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: dependencyName } }] : []), ...environmentQuery(environment), ]; }; diff --git a/x-pack/plugins/observability_solution/apm/common/environment_filter_values.ts b/x-pack/plugins/observability_solution/apm/common/environment_filter_values.ts index 24bc00658a7d4..9faf645c54151 100644 --- a/x-pack/plugins/observability_solution/apm/common/environment_filter_values.ts +++ b/x-pack/plugins/observability_solution/apm/common/environment_filter_values.ts @@ -13,12 +13,9 @@ import { Environment } from './environment_rt'; const ENVIRONMENT_ALL_VALUE = 'ENVIRONMENT_ALL' as const; const ENVIRONMENT_NOT_DEFINED_VALUE = 'ENVIRONMENT_NOT_DEFINED' as const; -export const allOptionText = i18n.translate( - 'xpack.apm.filter.environment.allLabel', - { - defaultMessage: 'All', - } -); +export const allOptionText = i18n.translate('xpack.apm.filter.environment.allLabel', { + defaultMessage: 'All', +}); export function getEnvironmentLabel(environment: string): string { if (!environment || environment === ENVIRONMENT_NOT_DEFINED_VALUE) { @@ -80,10 +77,8 @@ export function getNextEnvironmentUrlParam({ requestedEnvironment?: string; currentEnvironmentUrlParam: Environment; }) { - const normalizedRequestedEnvironment = - requestedEnvironment || ENVIRONMENT_NOT_DEFINED.value; - const normalizedQueryEnvironment = - currentEnvironmentUrlParam || ENVIRONMENT_ALL.value; + const normalizedRequestedEnvironment = requestedEnvironment || ENVIRONMENT_NOT_DEFINED.value; + const normalizedQueryEnvironment = currentEnvironmentUrlParam || ENVIRONMENT_ALL.value; if (normalizedRequestedEnvironment === normalizedQueryEnvironment) { return currentEnvironmentUrlParam || ENVIRONMENT_ALL.value; diff --git a/x-pack/plugins/observability_solution/apm/common/environment_rt.ts b/x-pack/plugins/observability_solution/apm/common/environment_rt.ts index dd07bb9f47318..530b6e7930134 100644 --- a/x-pack/plugins/observability_solution/apm/common/environment_rt.ts +++ b/x-pack/plugins/observability_solution/apm/common/environment_rt.ts @@ -6,10 +6,7 @@ */ import * as t from 'io-ts'; import { nonEmptyStringRt } from '@kbn/io-ts-utils'; -import { - ENVIRONMENT_ALL, - ENVIRONMENT_NOT_DEFINED, -} from './environment_filter_values'; +import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED } from './environment_filter_values'; export const environmentStringRt = t.union([ t.literal(ENVIRONMENT_NOT_DEFINED.value), diff --git a/x-pack/plugins/observability_solution/apm/common/es_fields/apm.ts b/x-pack/plugins/observability_solution/apm/common/es_fields/apm.ts index 05efeceb11f28..27a786df210e4 100644 --- a/x-pack/plugins/observability_solution/apm/common/es_fields/apm.ts +++ b/x-pack/plugins/observability_solution/apm/common/es_fields/apm.ts @@ -36,8 +36,7 @@ export const SERVICE_RUNTIME_VERSION = 'service.runtime.version'; export const SERVICE_NODE_NAME = 'service.node.name'; export const SERVICE_VERSION = 'service.version'; export const SERVICE_TARGET_TYPE = 'service.target.type'; -export const SERVICE_OVERFLOW_COUNT = - 'service_transaction.aggregation.overflow_count'; +export const SERVICE_OVERFLOW_COUNT = 'service_transaction.aggregation.overflow_count'; export const URL_FULL = 'url.full'; export const HTTP_REQUEST_METHOD = 'http.request.method'; @@ -61,12 +60,10 @@ export const TRANSACTION_SAMPLED = 'transaction.sampled'; export const TRANSACTION_PAGE_URL = 'transaction.page.url'; export const TRANSACTION_FAILURE_COUNT = 'transaction.failure_count'; export const TRANSACTION_SUCCESS_COUNT = 'transaction.success_count'; -export const TRANSACTION_OVERFLOW_COUNT = - 'transaction.aggregation.overflow_count'; +export const TRANSACTION_OVERFLOW_COUNT = 'transaction.aggregation.overflow_count'; // for transaction metrics export const TRANSACTION_ROOT = 'transaction.root'; -export const TRANSACTION_PROFILER_STACK_TRACE_IDS = - 'transaction.profiler_stack_trace_ids'; +export const TRANSACTION_PROFILER_STACK_TRACE_IDS = 'transaction.profiler_stack_trace_ids'; export const EVENT_OUTCOME = 'event.outcome'; @@ -79,8 +76,7 @@ export const SPAN_SELF_TIME_SUM = 'span.self_time.sum.us'; export const SPAN_ACTION = 'span.action'; export const SPAN_NAME = 'span.name'; export const SPAN_ID = 'span.id'; -export const SPAN_DESTINATION_SERVICE_RESOURCE = - 'span.destination.service.resource'; +export const SPAN_DESTINATION_SERVICE_RESOURCE = 'span.destination.service.resource'; export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT = 'span.destination.service.response_time.count'; @@ -93,8 +89,7 @@ export const SPAN_LINKS_SPAN_ID = 'span.links.span.id'; export const SPAN_COMPOSITE_COUNT = 'span.composite.count'; export const SPAN_COMPOSITE_SUM = 'span.composite.sum.us'; -export const SPAN_COMPOSITE_COMPRESSION_STRATEGY = - 'span.composite.compression_strategy'; +export const SPAN_COMPOSITE_COMPRESSION_STRATEGY = 'span.composite.compression_strategy'; export const SPAN_SYNC = 'span.sync'; @@ -119,17 +114,14 @@ export const METRIC_SYSTEM_FREE_MEMORY = 'system.memory.actual.free'; export const METRIC_SYSTEM_TOTAL_MEMORY = 'system.memory.total'; export const METRIC_SYSTEM_CPU_PERCENT = 'system.cpu.total.norm.pct'; export const METRIC_PROCESS_CPU_PERCENT = 'system.process.cpu.total.norm.pct'; -export const METRIC_CGROUP_MEMORY_LIMIT_BYTES = - 'system.process.cgroup.memory.mem.limit.bytes'; -export const METRIC_CGROUP_MEMORY_USAGE_BYTES = - 'system.process.cgroup.memory.mem.usage.bytes'; +export const METRIC_CGROUP_MEMORY_LIMIT_BYTES = 'system.process.cgroup.memory.mem.limit.bytes'; +export const METRIC_CGROUP_MEMORY_USAGE_BYTES = 'system.process.cgroup.memory.mem.usage.bytes'; export const METRIC_JAVA_HEAP_MEMORY_MAX = 'jvm.memory.heap.max'; export const METRIC_JAVA_HEAP_MEMORY_COMMITTED = 'jvm.memory.heap.committed'; export const METRIC_JAVA_HEAP_MEMORY_USED = 'jvm.memory.heap.used'; export const METRIC_JAVA_NON_HEAP_MEMORY_MAX = 'jvm.memory.non_heap.max'; -export const METRIC_JAVA_NON_HEAP_MEMORY_COMMITTED = - 'jvm.memory.non_heap.committed'; +export const METRIC_JAVA_NON_HEAP_MEMORY_COMMITTED = 'jvm.memory.non_heap.committed'; export const METRIC_JAVA_NON_HEAP_MEMORY_USED = 'jvm.memory.non_heap.used'; export const METRIC_JAVA_THREAD_COUNT = 'jvm.thread.count'; export const METRIC_JAVA_GC_COUNT = 'jvm.gc.count'; @@ -169,21 +161,14 @@ export const FAAS_BILLED_DURATION = 'faas.billed_duration'; // OpenTelemetry Metrics export const METRIC_OTEL_SYSTEM_CPU_UTILIZATION = 'system.cpu.utilization'; -export const METRIC_OTEL_SYSTEM_MEMORY_UTILIZATION = - 'system.memory.utilization'; - -export const METRIC_OTEL_JVM_PROCESS_CPU_PERCENT = - 'process.runtime.jvm.cpu.utilization'; -export const METRIC_OTEL_JVM_PROCESS_MEMORY_USAGE = - 'process.runtime.jvm.memory.usage'; -export const METRIC_OTEL_JVM_PROCESS_MEMORY_COMMITTED = - 'process.runtime.jvm.memory.committed'; -export const METRIC_OTEL_JVM_PROCESS_MEMORY_LIMIT = - 'process.runtime.jvm.memory.limit'; -export const METRIC_OTEL_JVM_PROCESS_THREADS_COUNT = - 'process.runtime.jvm.threads.count'; -export const METRIC_OTEL_JVM_SYSTEM_CPU_PERCENT = - 'process.runtime.jvm.system.cpu.utilization'; +export const METRIC_OTEL_SYSTEM_MEMORY_UTILIZATION = 'system.memory.utilization'; + +export const METRIC_OTEL_JVM_PROCESS_CPU_PERCENT = 'process.runtime.jvm.cpu.utilization'; +export const METRIC_OTEL_JVM_PROCESS_MEMORY_USAGE = 'process.runtime.jvm.memory.usage'; +export const METRIC_OTEL_JVM_PROCESS_MEMORY_COMMITTED = 'process.runtime.jvm.memory.committed'; +export const METRIC_OTEL_JVM_PROCESS_MEMORY_LIMIT = 'process.runtime.jvm.memory.limit'; +export const METRIC_OTEL_JVM_PROCESS_THREADS_COUNT = 'process.runtime.jvm.threads.count'; +export const METRIC_OTEL_JVM_SYSTEM_CPU_PERCENT = 'process.runtime.jvm.system.cpu.utilization'; export const METRIC_OTEL_JVM_GC_DURATION = 'process.runtime.jvm.gc.duration'; export const VALUE_OTEL_JVM_PROCESS_MEMORY_HEAP = 'heap'; export const VALUE_OTEL_JVM_PROCESS_MEMORY_NON_HEAP = 'non_heap'; diff --git a/x-pack/plugins/observability_solution/apm/common/es_fields/es_fields.test.ts b/x-pack/plugins/observability_solution/apm/common/es_fields/es_fields.test.ts index 416a7f6fc39e8..f33fddd430e8d 100644 --- a/x-pack/plugins/observability_solution/apm/common/es_fields/es_fields.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/es_fields/es_fields.test.ts @@ -194,9 +194,7 @@ describe('Error', () => { matchSnapshot(errorDoc); }); -function matchSnapshot( - obj: AllowUnknownProperties -) { +function matchSnapshot(obj: AllowUnknownProperties) { Object.entries(fieldnames).forEach(([key, longKey]) => { const value = get(obj, longKey); it(key, () => { diff --git a/x-pack/plugins/observability_solution/apm/common/fleet.ts b/x-pack/plugins/observability_solution/apm/common/fleet.ts index 5812f438de174..5f64255c7ad54 100644 --- a/x-pack/plugins/observability_solution/apm/common/fleet.ts +++ b/x-pack/plugins/observability_solution/apm/common/fleet.ts @@ -42,22 +42,17 @@ export const INPUT_VAR_NAME_TO_SCHEMA_PATH: Record = { anonymous_enabled: 'apm-server.auth.anonymous.enabled', anonymous_allow_agent: 'apm-server.auth.anonymous.allow_agent', anonymous_allow_service: 'apm-server.auth.anonymous.allow_service', - anonymous_rate_limit_ip_limit: - 'apm-server.auth.anonymous.rate_limit.ip_limit', - anonymous_rate_limit_event_limit: - 'apm-server.auth.anonymous.rate_limit.event_limit', + anonymous_rate_limit_ip_limit: 'apm-server.auth.anonymous.rate_limit.ip_limit', + anonymous_rate_limit_event_limit: 'apm-server.auth.anonymous.rate_limit.event_limit', tail_sampling_enabled: 'apm-server.sampling.tail.enabled', tail_sampling_interval: 'apm-server.sampling.tail.interval', tail_sampling_policies: 'apm-server.sampling.tail.policies', }; export const LEGACY_TO_CURRENT_SCHEMA_PATHS: Record = { - 'apm-server.rum.event_rate.limit': - 'apm-server.auth.anonymous.rate_limit.event_limit', - 'apm-server.rum.event_rate.lru_size': - 'apm-server.auth.anonymous.rate_limit.ip_limit', - 'apm-server.rum.allow_service_names': - 'apm-server.auth.anonymous.allow_service', + 'apm-server.rum.event_rate.limit': 'apm-server.auth.anonymous.rate_limit.event_limit', + 'apm-server.rum.event_rate.lru_size': 'apm-server.auth.anonymous.rate_limit.ip_limit', + 'apm-server.rum.allow_service_names': 'apm-server.auth.anonymous.allow_service', 'apm-server.secret_token': 'apm-server.auth.secret_token', 'apm-server.api_key.enabled': 'apm-server.auth.api_key.enabled', }; diff --git a/x-pack/plugins/observability_solution/apm/common/i18n.ts b/x-pack/plugins/observability_solution/apm/common/i18n.ts index 8bce2acdf4dca..b52bdd582bd10 100644 --- a/x-pack/plugins/observability_solution/apm/common/i18n.ts +++ b/x-pack/plugins/observability_solution/apm/common/i18n.ts @@ -7,9 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const NOT_AVAILABLE_LABEL = i18n.translate( - 'xpack.apm.notAvailableLabel', - { - defaultMessage: 'N/A', - } -); +export const NOT_AVAILABLE_LABEL = i18n.translate('xpack.apm.notAvailableLabel', { + defaultMessage: 'N/A', +}); diff --git a/x-pack/plugins/observability_solution/apm/common/latency_aggregation_types.ts b/x-pack/plugins/observability_solution/apm/common/latency_aggregation_types.ts index 21177d9d1f722..d9466832e4ff2 100644 --- a/x-pack/plugins/observability_solution/apm/common/latency_aggregation_types.ts +++ b/x-pack/plugins/observability_solution/apm/common/latency_aggregation_types.ts @@ -22,6 +22,5 @@ export const latencyAggregationTypeRt = t.union([ export const getLatencyAggregationType = ( latencyAggregationType: string | null | undefined ): LatencyAggregationType => { - return (latencyAggregationType ?? - LatencyAggregationType.avg) as LatencyAggregationType; + return (latencyAggregationType ?? LatencyAggregationType.avg) as LatencyAggregationType; }; diff --git a/x-pack/plugins/observability_solution/apm/common/privilege_type.ts b/x-pack/plugins/observability_solution/apm/common/privilege_type.ts index 4cb7831b42c02..cde4de5565d9f 100644 --- a/x-pack/plugins/observability_solution/apm/common/privilege_type.ts +++ b/x-pack/plugins/observability_solution/apm/common/privilege_type.ts @@ -17,8 +17,5 @@ export enum ClusterPrivilegeType { } export const privilegesTypeRt = t.array( - t.union([ - t.literal(PrivilegeType.EVENT), - t.literal(PrivilegeType.AGENT_CONFIG), - ]) + t.union([t.literal(PrivilegeType.EVENT), t.literal(PrivilegeType.AGENT_CONFIG)]) ); diff --git a/x-pack/plugins/observability_solution/apm/common/rules/apm_rule_types.ts b/x-pack/plugins/observability_solution/apm/common/rules/apm_rule_types.ts index c2712fb972234..fe93b024bb2b8 100644 --- a/x-pack/plugins/observability_solution/apm/common/rules/apm_rule_types.ts +++ b/x-pack/plugins/observability_solution/apm/common/rules/apm_rule_types.ts @@ -7,11 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { ValuesType } from 'utility-types'; -import type { - AsDuration, - AsPercent, - TimeUnitChar, -} from '@kbn/observability-plugin/common'; +import type { AsDuration, AsPercent, TimeUnitChar } from '@kbn/observability-plugin/common'; import type { ActionGroup } from '@kbn/alerting-plugin/common'; import { formatDurationFromTimeUnitChar } from '@kbn/observability-plugin/common'; import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity'; @@ -64,22 +60,13 @@ const getFieldNameLabel = (field: string): string => { } }; -export const getFieldValueLabel = ( - field: string, - fieldValue: string -): string => { - return field === SERVICE_ENVIRONMENT - ? getEnvironmentLabel(fieldValue) - : fieldValue; +export const getFieldValueLabel = (field: string, fieldValue: string): string => { + return field === SERVICE_ENVIRONMENT ? getEnvironmentLabel(fieldValue) : fieldValue; }; const formatGroupByFields = (groupByFields: Record): string => { const groupByFieldLabels = Object.keys(groupByFields).map( - (field) => - `${getFieldNameLabel(field)}: ${getFieldValueLabel( - field, - groupByFields[field] - )}` + (field) => `${getFieldNameLabel(field)}: ${getFieldValueLabel(field, groupByFields[field])}` ); return groupByFieldLabels.join(', '); }; @@ -102,10 +89,7 @@ export function formatErrorCountReason({ values: { threshold, measured, - interval: formatDurationFromTimeUnitChar( - windowSize, - windowUnit as TimeUnitChar - ), + interval: formatDurationFromTimeUnitChar(windowSize, windowUnit as TimeUnitChar), group: formatGroupByFields(groupByFields), }, }); @@ -128,10 +112,8 @@ export function formatTransactionDurationReason({ windowUnit: string; groupByFields: Record; }) { - let aggregationTypeFormatted = - aggregationType.charAt(0).toUpperCase() + aggregationType.slice(1); - if (aggregationTypeFormatted === 'Avg') - aggregationTypeFormatted = aggregationTypeFormatted + '.'; + let aggregationTypeFormatted = aggregationType.charAt(0).toUpperCase() + aggregationType.slice(1); + if (aggregationTypeFormatted === 'Avg') aggregationTypeFormatted = aggregationTypeFormatted + '.'; return i18n.translate('xpack.apm.alertTypes.transactionDuration.reason', { defaultMessage: `{aggregationType} latency is {measured} in the last {interval} for {group}. Alert when > {threshold}.`, @@ -139,10 +121,7 @@ export function formatTransactionDurationReason({ threshold: asDuration(threshold), measured: asDuration(measured), aggregationType: aggregationTypeFormatted, - interval: formatDurationFromTimeUnitChar( - windowSize, - windowUnit as TimeUnitChar - ), + interval: formatDurationFromTimeUnitChar(windowSize, windowUnit as TimeUnitChar), group: formatGroupByFields(groupByFields), }, }); @@ -168,10 +147,7 @@ export function formatTransactionErrorRateReason({ values: { threshold: asPercent(threshold, 100), measured: asPercent(measured, 100), - interval: formatDurationFromTimeUnitChar( - windowSize, - windowUnit as TimeUnitChar - ), + interval: formatDurationFromTimeUnitChar(windowSize, windowUnit as TimeUnitChar), group: formatGroupByFields(groupByFields), }, }); @@ -192,22 +168,16 @@ export function formatAnomalyReason({ windowUnit: string; detectorType: AnomalyDetectorType; }) { - return i18n.translate( - 'xpack.apm.alertTypes.transactionDurationAnomaly.reason', - { - defaultMessage: `{severityLevel} {detectorTypeLabel} anomaly with a score of {anomalyScore}, was detected in the last {interval} for {serviceName}.`, - values: { - serviceName, - severityLevel, - detectorTypeLabel: getApmMlDetectorLabel(detectorType), - anomalyScore, - interval: formatDurationFromTimeUnitChar( - windowSize, - windowUnit as TimeUnitChar - ), - }, - } - ); + return i18n.translate('xpack.apm.alertTypes.transactionDurationAnomaly.reason', { + defaultMessage: `{severityLevel} {detectorTypeLabel} anomaly with a score of {anomalyScore}, was detected in the last {interval} for {serviceName}.`, + values: { + serviceName, + severityLevel, + detectorTypeLabel: getApmMlDetectorLabel(detectorType), + anomalyScore, + interval: formatDurationFromTimeUnitChar(windowSize, windowUnit as TimeUnitChar), + }, + }); } export const RULE_TYPES_CONFIG: Record< @@ -294,9 +264,7 @@ export const ANOMALY_ALERT_SEVERITY_TYPES = [ }, ] as const; -export type AnomalyAlertSeverityType = ValuesType< - typeof ANOMALY_ALERT_SEVERITY_TYPES ->['type']; +export type AnomalyAlertSeverityType = ValuesType['type']; export function getApmMlDetectorLabel(type: AnomalyDetectorType) { switch (type) { @@ -305,19 +273,13 @@ export function getApmMlDetectorLabel(type: AnomalyDetectorType) { defaultMessage: 'latency', }); case AnomalyDetectorType.txThroughput: - return i18n.translate( - 'xpack.apm.alerts.anomalyDetector.throughputLabel', - { - defaultMessage: 'throughput', - } - ); + return i18n.translate('xpack.apm.alerts.anomalyDetector.throughputLabel', { + defaultMessage: 'throughput', + }); case AnomalyDetectorType.txFailureRate: - return i18n.translate( - 'xpack.apm.alerts.anomalyDetector.failedTransactionRateLabel', - { - defaultMessage: 'failed transaction rate', - } - ); + return i18n.translate('xpack.apm.alerts.anomalyDetector.failedTransactionRateLabel', { + defaultMessage: 'failed transaction rate', + }); } } diff --git a/x-pack/plugins/observability_solution/apm/common/rules/get_all_groupby_fields.ts b/x-pack/plugins/observability_solution/apm/common/rules/get_all_groupby_fields.ts index 055fb1984defe..c94ea1ba75887 100644 --- a/x-pack/plugins/observability_solution/apm/common/rules/get_all_groupby_fields.ts +++ b/x-pack/plugins/observability_solution/apm/common/rules/get_all_groupby_fields.ts @@ -7,11 +7,7 @@ import { union } from 'lodash'; import { ApmRuleType } from '@kbn/rule-data-utils'; -import { - SERVICE_ENVIRONMENT, - SERVICE_NAME, - TRANSACTION_TYPE, -} from '../es_fields/apm'; +import { SERVICE_ENVIRONMENT, SERVICE_NAME, TRANSACTION_TYPE } from '../es_fields/apm'; export const getAllGroupByFields = ( ruleType: string, @@ -22,11 +18,7 @@ export const getAllGroupByFields = ( switch (ruleType) { case ApmRuleType.TransactionDuration: case ApmRuleType.TransactionErrorRate: - predefinedGroupByFields = [ - SERVICE_NAME, - SERVICE_ENVIRONMENT, - TRANSACTION_TYPE, - ]; + predefinedGroupByFields = [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE]; break; case ApmRuleType.ErrorCount: predefinedGroupByFields = [SERVICE_NAME, SERVICE_ENVIRONMENT]; diff --git a/x-pack/plugins/observability_solution/apm/common/rules/schema.ts b/x-pack/plugins/observability_solution/apm/common/rules/schema.ts index 64a8ebbfd3146..0e6b0c166a688 100644 --- a/x-pack/plugins/observability_solution/apm/common/rules/schema.ts +++ b/x-pack/plugins/observability_solution/apm/common/rules/schema.ts @@ -13,10 +13,7 @@ import { AggregationType } from './apm_rule_types'; export const searchConfigurationSchema = schema.object({ query: schema.object({ - query: schema.oneOf([ - schema.string(), - schema.recordOf(schema.string(), schema.any()), - ]), + query: schema.oneOf([schema.string(), schema.recordOf(schema.string(), schema.any())]), language: schema.string(), }), }); @@ -69,9 +66,7 @@ export const anomalyParamsSchema = schema.object({ schema.literal(ML_ANOMALY_SEVERITY.MINOR), schema.literal(ML_ANOMALY_SEVERITY.WARNING), ]), - anomalyDetectorTypes: schema.maybe( - schema.arrayOf(detectorsSchema, { minSize: 1 }) - ), + anomalyDetectorTypes: schema.maybe(schema.arrayOf(detectorsSchema, { minSize: 1 })), }); export const transactionErrorRateParamsSchema = schema.object({ @@ -88,13 +83,9 @@ export const transactionErrorRateParamsSchema = schema.object({ }); type ErrorCountParamsType = TypeOf; -type TransactionDurationParamsType = TypeOf< - typeof transactionDurationParamsSchema ->; +type TransactionDurationParamsType = TypeOf; type AnomalyParamsType = TypeOf; -type TransactionErrorRateParamsType = TypeOf< - typeof transactionErrorRateParamsSchema ->; +type TransactionErrorRateParamsType = TypeOf; export type SearchConfigurationType = TypeOf; diff --git a/x-pack/plugins/observability_solution/apm/common/serverless.test.ts b/x-pack/plugins/observability_solution/apm/common/serverless.test.ts index 5473e9d735d86..e422dea4ac6fe 100644 --- a/x-pack/plugins/observability_solution/apm/common/serverless.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/serverless.test.ts @@ -13,14 +13,10 @@ describe('getServerlessFunctionNameFromId', () => { it('returns correct serverless function name', () => { expect( - getServerlessFunctionNameFromId( - 'arn:aws:lambda:us-west-2:123456789012:function:my-function' - ) + getServerlessFunctionNameFromId('arn:aws:lambda:us-west-2:123456789012:function:my-function') ).toEqual('my-function'); expect( - getServerlessFunctionNameFromId( - 'arn:aws:lambda:us-west-2:123456789012:function:my:function' - ) + getServerlessFunctionNameFromId('arn:aws:lambda:us-west-2:123456789012:function:my:function') ).toEqual('my:function'); }); }); diff --git a/x-pack/plugins/observability_solution/apm/common/serverless.ts b/x-pack/plugins/observability_solution/apm/common/serverless.ts index 42f2844e1c6ec..e6e91937f96fb 100644 --- a/x-pack/plugins/observability_solution/apm/common/serverless.ts +++ b/x-pack/plugins/observability_solution/apm/common/serverless.ts @@ -25,17 +25,11 @@ export function getServerlessTypeFromCloudData( cloudProvider?: string, cloudServiceName?: string ): ServerlessType | undefined { - if ( - cloudProvider?.toLowerCase() === 'aws' && - cloudServiceName?.toLowerCase() === 'lambda' - ) { + if (cloudProvider?.toLowerCase() === 'aws' && cloudServiceName?.toLowerCase() === 'lambda') { return ServerlessType.AWS_LAMBDA; } - if ( - cloudProvider?.toLowerCase() === 'azure' && - cloudServiceName?.toLowerCase() === 'functions' - ) { + if (cloudProvider?.toLowerCase() === 'azure' && cloudServiceName?.toLowerCase() === 'functions') { return ServerlessType.AZURE_FUNCTIONS; } } diff --git a/x-pack/plugins/observability_solution/apm/common/service_groups.test.ts b/x-pack/plugins/observability_solution/apm/common/service_groups.test.ts index 28c9120338ad5..0fbe784603ca2 100644 --- a/x-pack/plugins/observability_solution/apm/common/service_groups.test.ts +++ b/x-pack/plugins/observability_solution/apm/common/service_groups.test.ts @@ -10,11 +10,7 @@ import { validateServiceGroupKuery, SERVICE_GROUP_SUPPORTED_FIELDS, } from './service_groups'; -import { - TRANSACTION_TYPE, - TRANSACTION_DURATION, - SERVICE_FRAMEWORK_VERSION, -} from './es_fields/apm'; +import { TRANSACTION_TYPE, TRANSACTION_DURATION, SERVICE_FRAMEWORK_VERSION } from './es_fields/apm'; describe('service_groups common utils', () => { describe('isSupportedField', () => { @@ -24,11 +20,7 @@ describe('service_groups common utils', () => { }); }); it('should reject unsupported fields', () => { - const unsupportedFields = [ - TRANSACTION_TYPE, - TRANSACTION_DURATION, - SERVICE_FRAMEWORK_VERSION, - ]; + const unsupportedFields = [TRANSACTION_TYPE, TRANSACTION_DURATION, SERVICE_FRAMEWORK_VERSION]; unsupportedFields.map((field) => { expect(isSupportedField(field)).toBe(false); }); @@ -36,9 +28,7 @@ describe('service_groups common utils', () => { }); describe('validateServiceGroupKuery', () => { it('should validate supported KQL filter for a service group', () => { - const result = validateServiceGroupKuery( - `service.name: testbeans* or agent.name: "nodejs"` - ); + const result = validateServiceGroupKuery(`service.name: testbeans* or agent.name: "nodejs"`); expect(result).toHaveProperty('isValidFields', true); expect(result).toHaveProperty('isValidSyntax', true); expect(result).not.toHaveProperty('message'); @@ -55,9 +45,7 @@ describe('service_groups common utils', () => { ); }); it('should return parsing error when KQL is incomplete', () => { - const result = validateServiceGroupKuery( - `service.name: testbeans* or agent.name: "nod` - ); + const result = validateServiceGroupKuery(`service.name: testbeans* or agent.name: "nod`); expect(result).toHaveProperty('isValidFields', false); expect(result).toHaveProperty('isValidSyntax', false); expect(result).toHaveProperty('message'); diff --git a/x-pack/plugins/observability_solution/apm/common/service_groups.ts b/x-pack/plugins/observability_solution/apm/common/service_groups.ts index 382ef0f59a137..b93ecffc2ab5b 100644 --- a/x-pack/plugins/observability_solution/apm/common/service_groups.ts +++ b/x-pack/plugins/observability_solution/apm/common/service_groups.ts @@ -42,10 +42,7 @@ export const SERVICE_GROUP_SUPPORTED_FIELDS = [ ]; export function isSupportedField(fieldName: string) { - return ( - fieldName.startsWith(LABELS) || - SERVICE_GROUP_SUPPORTED_FIELDS.includes(fieldName) - ); + return fieldName.startsWith(LABELS) || SERVICE_GROUP_SUPPORTED_FIELDS.includes(fieldName); } export function validateServiceGroupKuery(kuery: string): { @@ -55,9 +52,7 @@ export function validateServiceGroupKuery(kuery: string): { } { try { const kueryFields = getKueryFields([fromKueryExpression(kuery)]); - const unsupportedKueryFields = kueryFields.filter( - (fieldName) => !isSupportedField(fieldName) - ); + const unsupportedKueryFields = kueryFields.filter((fieldName) => !isSupportedField(fieldName)); if (unsupportedKueryFields.length === 0) { return { isValidFields: true, isValidSyntax: true }; } diff --git a/x-pack/plugins/observability_solution/apm/common/service_health_status.ts b/x-pack/plugins/observability_solution/apm/common/service_health_status.ts index 77964d78c7450..7f5530aa7fa05 100644 --- a/x-pack/plugins/observability_solution/apm/common/service_health_status.ts +++ b/x-pack/plugins/observability_solution/apm/common/service_health_status.ts @@ -16,11 +16,7 @@ export enum ServiceHealthStatus { unknown = 'unknown', } -export function getServiceHealthStatus({ - severity, -}: { - severity: ML_ANOMALY_SEVERITY; -}) { +export function getServiceHealthStatus({ severity }: { severity: ML_ANOMALY_SEVERITY }) { switch (severity) { case ML_ANOMALY_SEVERITY.CRITICAL: case ML_ANOMALY_SEVERITY.MAJOR: @@ -38,10 +34,7 @@ export function getServiceHealthStatus({ } } -export function getServiceHealthStatusColor( - theme: EuiTheme, - status: ServiceHealthStatus -) { +export function getServiceHealthStatusColor(theme: EuiTheme, status: ServiceHealthStatus) { switch (status) { case ServiceHealthStatus.healthy: return theme.eui.euiColorVis0; @@ -54,10 +47,7 @@ export function getServiceHealthStatusColor( } } -export function getServiceHealthStatusBadgeColor( - theme: EuiTheme, - status: ServiceHealthStatus -) { +export function getServiceHealthStatusBadgeColor(theme: EuiTheme, status: ServiceHealthStatus) { switch (status) { case ServiceHealthStatus.healthy: return theme.eui.euiColorVis0_behindText; diff --git a/x-pack/plugins/observability_solution/apm/common/service_map.ts b/x-pack/plugins/observability_solution/apm/common/service_map.ts index fb2fa7fae6540..c4ac295206396 100644 --- a/x-pack/plugins/observability_solution/apm/common/service_map.ts +++ b/x-pack/plugins/observability_solution/apm/common/service_map.ts @@ -84,13 +84,10 @@ export interface NodeStats { }; } -export const invalidLicenseMessage = i18n.translate( - 'xpack.apm.serviceMap.invalidLicenseMessage', - { - defaultMessage: - "In order to access Service Maps, you must be subscribed to an Elastic Platinum license. With it, you'll have the ability to visualize your entire application stack along with your APM data.", - } -); +export const invalidLicenseMessage = i18n.translate('xpack.apm.serviceMap.invalidLicenseMessage', { + defaultMessage: + "In order to access Service Maps, you must be subscribed to an Elastic Platinum license. With it, you'll have the ability to visualize your entire application stack along with your APM data.", +}); const NONGROUPED_SPANS: Record = { aws: ['servicename'], @@ -106,8 +103,7 @@ export function isSpanGroupingSupported(type?: string, subtype?: string) { return true; } return !NONGROUPED_SPANS[type].some( - (nongroupedSubType) => - nongroupedSubType === 'all' || nongroupedSubType === subtype + (nongroupedSubType) => nongroupedSubType === 'all' || nongroupedSubType === subtype ); } diff --git a/x-pack/plugins/observability_solution/apm/common/service_nodes.ts b/x-pack/plugins/observability_solution/apm/common/service_nodes.ts index ad75bd025069d..e650f37e9ab74 100644 --- a/x-pack/plugins/observability_solution/apm/common/service_nodes.ts +++ b/x-pack/plugins/observability_solution/apm/common/service_nodes.ts @@ -9,12 +9,9 @@ import { i18n } from '@kbn/i18n'; export const SERVICE_NODE_NAME_MISSING = '_service_node_name_missing_'; -const UNIDENTIFIED_SERVICE_NODES_LABEL = i18n.translate( - 'xpack.apm.serviceNodeNameMissing', - { - defaultMessage: '(Empty)', - } -); +const UNIDENTIFIED_SERVICE_NODES_LABEL = i18n.translate('xpack.apm.serviceNodeNameMissing', { + defaultMessage: '(Empty)', +}); export function getServiceNodeName(serviceNodeName?: string) { return serviceNodeName === SERVICE_NODE_NAME_MISSING || !serviceNodeName diff --git a/x-pack/plugins/observability_solution/apm/common/time_range_metadata.ts b/x-pack/plugins/observability_solution/apm/common/time_range_metadata.ts index ff6d80ca1c745..f13ab5a89d6d1 100644 --- a/x-pack/plugins/observability_solution/apm/common/time_range_metadata.ts +++ b/x-pack/plugins/observability_solution/apm/common/time_range_metadata.ts @@ -9,7 +9,5 @@ import { ApmDataSource } from './data_source'; export interface TimeRangeMetadata { isUsingServiceDestinationMetrics: boolean; - sources: Array< - ApmDataSource & { hasDocs: boolean; hasDurationSummaryField: boolean } - >; + sources: Array; } diff --git a/x-pack/plugins/observability_solution/apm/common/tutorial/instructions/apm_agent_instructions.ts b/x-pack/plugins/observability_solution/apm/common/tutorial/instructions/apm_agent_instructions.ts index 56f8a179fb302..ff3d89a9f98ec 100644 --- a/x-pack/plugins/observability_solution/apm/common/tutorial/instructions/apm_agent_instructions.ts +++ b/x-pack/plugins/observability_solution/apm/common/tutorial/instructions/apm_agent_instructions.ts @@ -7,17 +7,13 @@ import { i18n } from '@kbn/i18n'; -export const createNodeAgentInstructions = ( - apmServerUrl = '', - secretToken = '' -) => [ +export const createNodeAgentInstructions = (apmServerUrl = '', secretToken = '') => [ { title: i18n.translate('xpack.apm.tutorial.nodeClient.install.title', { defaultMessage: 'Install the APM agent', }), textPre: i18n.translate('xpack.apm.tutorial.nodeClient.install.textPre', { - defaultMessage: - 'Install the APM agent for Node.js as a dependency to your application.', + defaultMessage: 'Install the APM agent for Node.js as a dependency to your application.', }), commands: ['npm install elastic-apm-node --save'], }, @@ -32,27 +28,20 @@ APM services are created programmatically based on the `serviceName`. \ This agent supports a variety of frameworks but can also be used with your custom stack.', }), customComponentName: 'TutorialConfigAgent', - textPost: i18n.translate( - 'xpack.apm.tutorial.nodeClient.configure.textPost', - { - defaultMessage: - 'See [the documentation]({documentationLink}) for advanced usage, including how to use with \ + textPost: i18n.translate('xpack.apm.tutorial.nodeClient.configure.textPost', { + defaultMessage: + 'See [the documentation]({documentationLink}) for advanced usage, including how to use with \ [Babel/ES Modules]({babelEsModulesLink}).', - values: { - documentationLink: - '{config.docs.base_url}guide/en/apm/agent/nodejs/current/index.html', - babelEsModulesLink: - '{config.docs.base_url}guide/en/apm/agent/nodejs/current/advanced-setup.html#es-modules', - }, - } - ), + values: { + documentationLink: '{config.docs.base_url}guide/en/apm/agent/nodejs/current/index.html', + babelEsModulesLink: + '{config.docs.base_url}guide/en/apm/agent/nodejs/current/advanced-setup.html#es-modules', + }, + }), }, ]; -export const createDjangoAgentInstructions = ( - apmServerUrl = '', - secretToken = '' -) => [ +export const createDjangoAgentInstructions = (apmServerUrl = '', secretToken = '') => [ { title: i18n.translate('xpack.apm.tutorial.djangoClient.install.title', { defaultMessage: 'Install the APM agent', @@ -66,33 +55,23 @@ export const createDjangoAgentInstructions = ( title: i18n.translate('xpack.apm.tutorial.djangoClient.configure.title', { defaultMessage: 'Configure the agent', }), - textPre: i18n.translate( - 'xpack.apm.tutorial.djangoClient.configure.textPre', - { - defaultMessage: - 'Agents are libraries that run inside of your application process. \ + textPre: i18n.translate('xpack.apm.tutorial.djangoClient.configure.textPre', { + defaultMessage: + 'Agents are libraries that run inside of your application process. \ APM services are created programmatically based on the `SERVICE_NAME`.', - } - ), + }), customComponentName: 'TutorialConfigAgent', - textPost: i18n.translate( - 'xpack.apm.tutorial.djangoClient.configure.textPost', - { - defaultMessage: - 'See the [documentation]({documentationLink}) for advanced usage.', - values: { - documentationLink: - '{config.docs.base_url}guide/en/apm/agent/python/current/django-support.html', - }, - } - ), + textPost: i18n.translate('xpack.apm.tutorial.djangoClient.configure.textPost', { + defaultMessage: 'See the [documentation]({documentationLink}) for advanced usage.', + values: { + documentationLink: + '{config.docs.base_url}guide/en/apm/agent/python/current/django-support.html', + }, + }), }, ]; -export const createFlaskAgentInstructions = ( - apmServerUrl = '', - secretToken = '' -) => [ +export const createFlaskAgentInstructions = (apmServerUrl = '', secretToken = '') => [ { title: i18n.translate('xpack.apm.tutorial.flaskClient.install.title', { defaultMessage: 'Install the APM agent', @@ -106,33 +85,23 @@ export const createFlaskAgentInstructions = ( title: i18n.translate('xpack.apm.tutorial.flaskClient.configure.title', { defaultMessage: 'Configure the agent', }), - textPre: i18n.translate( - 'xpack.apm.tutorial.flaskClient.configure.textPre', - { - defaultMessage: - 'Agents are libraries that run inside of your application process. \ + textPre: i18n.translate('xpack.apm.tutorial.flaskClient.configure.textPre', { + defaultMessage: + 'Agents are libraries that run inside of your application process. \ APM services are created programmatically based on the `SERVICE_NAME`.', - } - ), + }), customComponentName: 'TutorialConfigAgent', - textPost: i18n.translate( - 'xpack.apm.tutorial.flaskClient.configure.textPost', - { - defaultMessage: - 'See the [documentation]({documentationLink}) for advanced usage.', - values: { - documentationLink: - '{config.docs.base_url}guide/en/apm/agent/python/current/flask-support.html', - }, - } - ), + textPost: i18n.translate('xpack.apm.tutorial.flaskClient.configure.textPost', { + defaultMessage: 'See the [documentation]({documentationLink}) for advanced usage.', + values: { + documentationLink: + '{config.docs.base_url}guide/en/apm/agent/python/current/flask-support.html', + }, + }), }, ]; -export const createRailsAgentInstructions = ( - apmServerUrl = '', - secretToken = '' -) => [ +export const createRailsAgentInstructions = (apmServerUrl = '', secretToken = '') => [ { title: i18n.translate('xpack.apm.tutorial.railsClient.install.title', { defaultMessage: 'Install the APM agent', @@ -146,33 +115,23 @@ export const createRailsAgentInstructions = ( title: i18n.translate('xpack.apm.tutorial.railsClient.configure.title', { defaultMessage: 'Configure the agent', }), - textPre: i18n.translate( - 'xpack.apm.tutorial.railsClient.configure.textPre', - { - defaultMessage: - 'APM is automatically started when your app boots. Configure the agent, by creating the config file {configFile}', - values: { configFile: '`config/elastic_apm.yml`' }, - } - ), + textPre: i18n.translate('xpack.apm.tutorial.railsClient.configure.textPre', { + defaultMessage: + 'APM is automatically started when your app boots. Configure the agent, by creating the config file {configFile}', + values: { configFile: '`config/elastic_apm.yml`' }, + }), customComponentName: 'TutorialConfigAgent', - textPost: i18n.translate( - 'xpack.apm.tutorial.railsClient.configure.textPost', - { - defaultMessage: - 'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n', - values: { - documentationLink: - '{config.docs.base_url}guide/en/apm/agent/ruby/current/index.html', - }, - } - ), + textPost: i18n.translate('xpack.apm.tutorial.railsClient.configure.textPost', { + defaultMessage: + 'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n', + values: { + documentationLink: '{config.docs.base_url}guide/en/apm/agent/ruby/current/index.html', + }, + }), }, ]; -export const createRackAgentInstructions = ( - apmServerUrl = '', - secretToken = '' -) => [ +export const createRackAgentInstructions = (apmServerUrl = '', secretToken = '') => [ { title: i18n.translate('xpack.apm.tutorial.rackClient.install.title', { defaultMessage: 'Install the APM agent', @@ -222,78 +181,56 @@ export const createRackAgentInstructions = ( title: i18n.translate('xpack.apm.tutorial.rackClient.createConfig.title', { defaultMessage: 'Create config file', }), - textPre: i18n.translate( - 'xpack.apm.tutorial.rackClient.createConfig.textPre', - { - defaultMessage: 'Create a config file {configFile}:', - values: { configFile: '`config/elastic_apm.yml`' }, - } - ), + textPre: i18n.translate('xpack.apm.tutorial.rackClient.createConfig.textPre', { + defaultMessage: 'Create a config file {configFile}:', + values: { configFile: '`config/elastic_apm.yml`' }, + }), customComponentName: 'TutorialConfigAgent', - textPost: i18n.translate( - 'xpack.apm.tutorial.rackClient.createConfig.textPost', - { - defaultMessage: - 'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n', - values: { - documentationLink: - '{config.docs.base_url}guide/en/apm/agent/ruby/current/index.html', - }, - } - ), + textPost: i18n.translate('xpack.apm.tutorial.rackClient.createConfig.textPost', { + defaultMessage: + 'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n', + values: { + documentationLink: '{config.docs.base_url}guide/en/apm/agent/ruby/current/index.html', + }, + }), }, ]; export const createJsAgentInstructions = (apmServerUrl = '') => [ { - title: i18n.translate( - 'xpack.apm.tutorial.jsClient.enableRealUserMonitoring.title', - { - defaultMessage: 'Enable Real User Monitoring support in APM Server', - } - ), - textPre: i18n.translate( - 'xpack.apm.tutorial.jsClient.enableRealUserMonitoring.textPre', - { - defaultMessage: - 'APM Server disables RUM support by default. See the [documentation]({documentationLink}) \ + title: i18n.translate('xpack.apm.tutorial.jsClient.enableRealUserMonitoring.title', { + defaultMessage: 'Enable Real User Monitoring support in APM Server', + }), + textPre: i18n.translate('xpack.apm.tutorial.jsClient.enableRealUserMonitoring.textPre', { + defaultMessage: + 'APM Server disables RUM support by default. See the [documentation]({documentationLink}) \ for details on how to enable RUM support. When using the APM integration with Fleet, RUM support is automatically enabled.', - values: { - documentationLink: - '{config.docs.base_url}guide/en/apm/guide/{config.docs.version}/configuration-rum.html', - }, - } - ), + values: { + documentationLink: + '{config.docs.base_url}guide/en/apm/guide/{config.docs.version}/configuration-rum.html', + }, + }), }, { - title: i18n.translate( - 'xpack.apm.tutorial.jsClient.installDependency.title', - { - defaultMessage: 'Set up the Agent as a dependency', - } - ), - textPre: i18n.translate( - 'xpack.apm.tutorial.jsClient.installDependency.textPre', - { - defaultMessage: - 'You can install the Agent as a dependency to your application with \ + title: i18n.translate('xpack.apm.tutorial.jsClient.installDependency.title', { + defaultMessage: 'Set up the Agent as a dependency', + }), + textPre: i18n.translate('xpack.apm.tutorial.jsClient.installDependency.textPre', { + defaultMessage: + 'You can install the Agent as a dependency to your application with \ `npm install @elastic/apm-rum --save`.\n\n\ The Agent can then be initialized and configured in your application like this:', - } - ), + }), customComponentName: 'TutorialConfigAgent', - textPost: i18n.translate( - 'xpack.apm.tutorial.jsClient.installDependency.textPost', - { - defaultMessage: - 'Framework integrations, like React or Angular, have custom dependencies. \ + textPost: i18n.translate('xpack.apm.tutorial.jsClient.installDependency.textPost', { + defaultMessage: + 'Framework integrations, like React or Angular, have custom dependencies. \ See the [integration documentation]({docLink}) for more information.', - values: { - docLink: - '{config.docs.base_url}guide/en/apm/agent/rum-js/current/framework-integrations.html', - }, - } - ), + values: { + docLink: + '{config.docs.base_url}guide/en/apm/agent/rum-js/current/framework-integrations.html', + }, + }), }, { title: i18n.translate('xpack.apm.tutorial.jsClient.scriptTags.title', { @@ -306,20 +243,15 @@ Add a `