diff --git a/docs/user/dashboard/aggregation-reference.asciidoc b/docs/user/dashboard/aggregation-reference.asciidoc index 39e596df4af34..001114578a1cd 100644 --- a/docs/user/dashboard/aggregation-reference.asciidoc +++ b/docs/user/dashboard/aggregation-reference.asciidoc @@ -23,7 +23,7 @@ This reference can help simplify the comparison if you need a specific feature. | Table with summary row ^| X -| +^| X | | | @@ -65,7 +65,7 @@ This reference can help simplify the comparison if you need a specific feature. | Heat map ^| X -| +^| X | | ^| X @@ -333,7 +333,7 @@ build their advanced visualization. | Math on aggregated data | -| +^| X ^| X ^| X ^| X @@ -352,6 +352,13 @@ build their advanced visualization. ^| X ^| X +| Time shifts +| +^| X +^| X +^| X +^| X + | Fully custom {es} queries | | diff --git a/docs/user/dashboard/create-panels-with-editors.asciidoc b/docs/user/dashboard/create-panels-with-editors.asciidoc index 17d3b5fb8a8a5..77a4706e249fd 100644 --- a/docs/user/dashboard/create-panels-with-editors.asciidoc +++ b/docs/user/dashboard/create-panels-with-editors.asciidoc @@ -30,13 +30,16 @@ [[lens-editor]] === Lens -*Lens* is the drag and drop editor that creates visualizations of your data. +*Lens* is the drag and drop editor that creates visualizations of your data, recommended for most +users. With *Lens*, you can: * Use the automatically generated suggestions to change the visualization type. * Create visualizations with multiple layers and indices. * Change the aggregation and labels to customize the data. +* Perform math on aggregations using *Formula*. +* Use time shifts to compare data at two times, such as month over month. [role="screenshot"] image:dashboard/images/lens_advanced_1_1.png[Lens] diff --git a/docs/user/dashboard/lens.asciidoc b/docs/user/dashboard/lens.asciidoc index 9f17a380bc209..7927489c596d7 100644 --- a/docs/user/dashboard/lens.asciidoc +++ b/docs/user/dashboard/lens.asciidoc @@ -300,7 +300,9 @@ image::images/lens_missing_values_strategy.png[Lens Missing values strategies me [[is-it-possible-to-change-the-scale-of-Y-axis]] ===== Is it possible to statically define the scale of the y-axis in a visualization? -The ability to start the y-axis from another value than 0, or use a logarithmic scale, is unsupported in *Lens*. +Yes, you can set the bounds on bar, line and area chart types in Lens, unless using percentage mode. Bar +and area charts must have 0 in the bounds. Logarithmic scales are unsupported in *Lens*. +To set the y-axis bounds, click the icon representing the axis you want to customize. [float] [[is-it-possible-to-have-pagination-for-datatable]] diff --git a/package.json b/package.json index ff2f62f513084..c9c6fa7f582c5 100644 --- a/package.json +++ b/package.json @@ -670,7 +670,7 @@ "callsites": "^3.1.0", "chai": "3.5.0", "chance": "1.0.18", - "chromedriver": "^90.0.0", + "chromedriver": "^91.0.1", "clean-webpack-plugin": "^3.0.0", "cmd-shim": "^2.1.0", "compression-webpack-plugin": "^4.0.0", @@ -838,4 +838,4 @@ "yargs": "^15.4.1", "zlib": "^1.0.5" } -} +} \ No newline at end of file diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx index 14086e6ee8137..1352081eaa30b 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx @@ -11,9 +11,7 @@ import React, { createContext, useContext } from 'react'; import { useRequest } from '../../../public'; -import { Error as CustomError } from './section_error'; - -import { Privileges } from '../types'; +import { Privileges, Error as CustomError } from '../types'; interface Authorization { isLoading: boolean; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts index b130602873724..f8eb7e3c7c0c8 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts @@ -16,4 +16,6 @@ export { WithPrivileges } from './with_privileges'; export { NotAuthorizedSection } from './not_authorized_section'; -export { Error, SectionError } from './section_error'; +export { SectionError } from './section_error'; + +export { PageError } from './page_error'; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/page_error.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/page_error.tsx new file mode 100644 index 0000000000000..0a27b4098681b --- /dev/null +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/page_error.tsx @@ -0,0 +1,72 @@ +/* + * 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 { EuiSpacer, EuiEmptyPrompt, EuiPageContent } from '@elastic/eui'; +import React from 'react'; +import { APP_WRAPPER_CLASS } from '../../../../../../src/core/public'; +import { Error } from '../types'; + +interface Props { + title: React.ReactNode; + error: Error; + actions?: JSX.Element; + isCentered?: boolean; +} + +/* + * A reusable component to handle full page errors. + * This is based on Kibana design guidelines related + * to the new management navigation structure. + * In some scenarios, it replaces the usage of . + */ + +export const PageError: React.FunctionComponent = ({ + title, + error, + actions, + isCentered, + ...rest +}) => { + const { + error: errorString, + cause, // wrapEsError() on the server adds a "cause" array + message, + } = error; + + const errorContent = ( + + {title}} + body={ + <> + {cause ? message || errorString :

{message || errorString}

} + {cause && ( + <> + +
    + {cause.map((causeMsg, i) => ( +
  • {causeMsg}
  • + ))} +
+ + )} + + } + iconType="alert" + actions={actions} + {...rest} + /> +
+ ); + + if (isCentered) { + return
{errorContent}
; + } + + return errorContent; +}; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx index c0b3533c8594b..a1652b4e153f5 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx @@ -8,12 +8,7 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import React, { Fragment } from 'react'; - -export interface Error { - error: string; - cause?: string[]; - message?: string; -} +import { Error } from '../types'; interface Props { title: React.ReactNode; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts index 089dc890c3e6c..e63d98512a2cd 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts @@ -12,8 +12,8 @@ export { AuthorizationProvider, AuthorizationContext, SectionError, - Error, + PageError, useAuthorizationContext, } from './components'; -export { Privileges, MissingPrivileges } from './types'; +export { Privileges, MissingPrivileges, Error } from './types'; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts index b10318aa415b3..70b54b0b6e425 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts @@ -14,3 +14,9 @@ export interface Privileges { hasAllPrivileges: boolean; missingPrivileges: MissingPrivileges; } + +export interface Error { + error: string; + cause?: string[]; + message?: string; +} diff --git a/src/plugins/es_ui_shared/public/authorization/index.ts b/src/plugins/es_ui_shared/public/authorization/index.ts index 483fffd9c4859..f68ad3da2a4b5 100644 --- a/src/plugins/es_ui_shared/public/authorization/index.ts +++ b/src/plugins/es_ui_shared/public/authorization/index.ts @@ -14,6 +14,7 @@ export { NotAuthorizedSection, Privileges, SectionError, + PageError, useAuthorizationContext, WithPrivileges, } from '../../__packages_do_not_import__/authorization'; diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts index b46a23994fe93..7b9013c043a0e 100644 --- a/src/plugins/es_ui_shared/public/index.ts +++ b/src/plugins/es_ui_shared/public/index.ts @@ -40,6 +40,7 @@ export { Privileges, MissingPrivileges, SectionError, + PageError, Error, useAuthorizationContext, } from './authorization'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts index 87e6ee62460fa..870e303a2930d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts @@ -16,18 +16,13 @@ import { engines } from '../../__mocks__/engines.mock'; import { nextTick } from '@kbn/test/jest'; import { asRoleMapping } from '../../../shared/role_mapping/__mocks__/roles'; -import { ANY_AUTH_PROVIDER, ROLE_MAPPING_NOT_FOUND } from '../../../shared/role_mapping/constants'; +import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; import { RoleMappingsLogic } from './role_mappings_logic'; describe('RoleMappingsLogic', () => { const { http } = mockHttpValues; - const { - clearFlashMessages, - flashAPIErrors, - setSuccessMessage, - setErrorMessage, - } = mockFlashMessageHelpers; + const { clearFlashMessages, flashAPIErrors, setSuccessMessage } = mockFlashMessageHelpers; const { mount } = new LogicMounter(RoleMappingsLogic); const DEFAULT_VALUES = { attributes: [], @@ -50,15 +45,14 @@ describe('RoleMappingsLogic', () => { roleMappingErrors: [], }; - const mappingsServerProps = { multipleAuthProvidersConfig: true, roleMappings: [asRoleMapping] }; - const mappingServerProps = { + const mappingsServerProps = { + multipleAuthProvidersConfig: true, + roleMappings: [asRoleMapping], attributes: ['email', 'metadata', 'username', 'role'], authProviders: [ANY_AUTH_PROVIDER], availableEngines: engines, elasticsearchRoles: [], hasAdvancedRoles: false, - multipleAuthProvidersConfig: false, - roleMapping: asRoleMapping, }; beforeEach(() => { @@ -75,48 +69,20 @@ describe('RoleMappingsLogic', () => { it('sets data based on server response from the `mappings` (plural) endpoint', () => { RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); - expect(RoleMappingsLogic.values.roleMappings).toEqual([asRoleMapping]); - expect(RoleMappingsLogic.values.dataLoading).toEqual(false); - expect(RoleMappingsLogic.values.multipleAuthProvidersConfig).toEqual(true); - }); - }); - - describe('setRoleMappingData', () => { - it('sets state based on server response from the `mapping` (singular) endpoint', () => { - RoleMappingsLogic.actions.setRoleMappingData(mappingServerProps); - expect(RoleMappingsLogic.values).toEqual({ ...DEFAULT_VALUES, - roleMapping: asRoleMapping, + roleMappings: [asRoleMapping], dataLoading: false, - attributes: mappingServerProps.attributes, - availableAuthProviders: mappingServerProps.authProviders, - availableEngines: mappingServerProps.availableEngines, + attributes: mappingsServerProps.attributes, + availableAuthProviders: mappingsServerProps.authProviders, + availableEngines: mappingsServerProps.availableEngines, accessAllEngines: true, - attributeName: 'role', - attributeValue: 'superuser', - elasticsearchRoles: mappingServerProps.elasticsearchRoles, - selectedEngines: new Set(engines.map((e) => e.name)), - selectedOptions: [ - { label: engines[0].name, value: engines[0].name }, - { label: engines[1].name, value: engines[1].name }, - ], - }); - }); - - it('will remove all selected engines if no roleMapping was returned from the server', () => { - RoleMappingsLogic.actions.setRoleMappingData({ - ...mappingServerProps, - roleMapping: undefined, - }); - - expect(RoleMappingsLogic.values).toEqual({ - ...DEFAULT_VALUES, - dataLoading: false, + multipleAuthProvidersConfig: true, + attributeName: 'username', + attributeValue: '', + elasticsearchRoles: mappingsServerProps.elasticsearchRoles, selectedEngines: new Set(), - attributes: mappingServerProps.attributes, - availableAuthProviders: mappingServerProps.authProviders, - availableEngines: mappingServerProps.availableEngines, + selectedOptions: [], }); }); }); @@ -135,11 +101,13 @@ describe('RoleMappingsLogic', () => { const engine = engines[0]; const otherEngine = engines[1]; const mountedValues = { - ...mappingServerProps, - roleMapping: { - ...asRoleMapping, - engines: [engine, otherEngine], - }, + ...mappingsServerProps, + roleMappings: [ + { + ...asRoleMapping, + engines: [engine, otherEngine], + }, + ], selectedEngines: new Set([engine.name]), }; @@ -153,11 +121,18 @@ describe('RoleMappingsLogic', () => { expect(RoleMappingsLogic.values.selectedEngines).toEqual( new Set([engine.name, otherEngine.name]) ); + expect(RoleMappingsLogic.values.selectedOptions).toEqual([ + { label: engine.name, value: engine.name }, + { label: otherEngine.name, value: otherEngine.name }, + ]); }); it('handles removing an engine from selected engines', () => { RoleMappingsLogic.actions.handleEngineSelectionChange([engine.name]); expect(RoleMappingsLogic.values.selectedEngines).toEqual(new Set([engine.name])); + expect(RoleMappingsLogic.values.selectedOptions).toEqual([ + { label: engine.name, value: engine.name }, + ]); }); }); @@ -175,17 +150,19 @@ describe('RoleMappingsLogic', () => { it('sets values correctly', () => { mount({ - ...mappingServerProps, + ...mappingsServerProps, elasticsearchRoles, }); RoleMappingsLogic.actions.handleAttributeSelectorChange('role', elasticsearchRoles[0]); expect(RoleMappingsLogic.values).toEqual({ ...DEFAULT_VALUES, + multipleAuthProvidersConfig: true, attributeValue: elasticsearchRoles[0], - roleMapping: asRoleMapping, - attributes: mappingServerProps.attributes, - availableEngines: mappingServerProps.availableEngines, + roleMappings: [asRoleMapping], + roleMapping: null, + attributes: mappingsServerProps.attributes, + availableEngines: mappingsServerProps.availableEngines, accessAllEngines: true, attributeName: 'role', elasticsearchRoles, @@ -215,11 +192,13 @@ describe('RoleMappingsLogic', () => { describe('handleAuthProviderChange', () => { beforeEach(() => { mount({ - ...mappingServerProps, - roleMapping: { - ...asRoleMapping, - authProvider: ['foo'], - }, + ...mappingsServerProps, + roleMappings: [ + { + ...asRoleMapping, + authProvider: ['foo'], + }, + ], }); }); const providers = ['bar', 'baz']; @@ -244,11 +223,13 @@ describe('RoleMappingsLogic', () => { it('handles "any" auth in previous state', () => { mount({ - ...mappingServerProps, - roleMapping: { - ...asRoleMapping, - authProvider: [ANY_AUTH_PROVIDER], - }, + ...mappingsServerProps, + roleMappings: [ + { + ...asRoleMapping, + authProvider: [ANY_AUTH_PROVIDER], + }, + ], }); RoleMappingsLogic.actions.handleAuthProviderChange(providerWithAny); @@ -258,7 +239,6 @@ describe('RoleMappingsLogic', () => { it('resetState', () => { mount(mappingsServerProps); - mount(mappingServerProps); RoleMappingsLogic.actions.resetState(); expect(RoleMappingsLogic.values).toEqual(DEFAULT_VALUES); @@ -266,7 +246,7 @@ describe('RoleMappingsLogic', () => { }); it('openRoleMappingFlyout', () => { - mount(mappingServerProps); + mount(mappingsServerProps); RoleMappingsLogic.actions.openRoleMappingFlyout(); expect(RoleMappingsLogic.values.roleMappingFlyoutOpen).toEqual(true); @@ -275,7 +255,7 @@ describe('RoleMappingsLogic', () => { it('closeRoleMappingFlyout', () => { mount({ - ...mappingServerProps, + ...mappingsServerProps, roleMappingFlyoutOpen: true, }); RoleMappingsLogic.actions.closeRoleMappingFlyout(); @@ -307,40 +287,20 @@ describe('RoleMappingsLogic', () => { }); describe('initializeRoleMapping', () => { - it('calls API and sets values for new mapping', async () => { - const setRoleMappingDataSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMappingData'); - http.get.mockReturnValue(Promise.resolve(mappingServerProps)); - RoleMappingsLogic.actions.initializeRoleMapping(); - - expect(http.get).toHaveBeenCalledWith('/api/app_search/role_mappings/new'); - await nextTick(); - expect(setRoleMappingDataSpy).toHaveBeenCalledWith(mappingServerProps); - }); - - it('calls API and sets values for existing mapping', async () => { - const setRoleMappingDataSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMappingData'); - http.get.mockReturnValue(Promise.resolve(mappingServerProps)); - RoleMappingsLogic.actions.initializeRoleMapping('123'); - - expect(http.get).toHaveBeenCalledWith('/api/app_search/role_mappings/123'); - await nextTick(); - expect(setRoleMappingDataSpy).toHaveBeenCalledWith(mappingServerProps); - }); - - it('handles error', async () => { - http.get.mockReturnValue(Promise.reject('this is an error')); - RoleMappingsLogic.actions.initializeRoleMapping(); - await nextTick(); + it('sets values for existing mapping', () => { + const setRoleMappingDataSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMapping'); + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + RoleMappingsLogic.actions.initializeRoleMapping(asRoleMapping.id); - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + expect(setRoleMappingDataSpy).toHaveBeenCalledWith(asRoleMapping); }); - it('shows error when there is a 404 status', async () => { - http.get.mockReturnValue(Promise.reject({ status: 404 })); + it('does not set data for new mapping', async () => { + const setRoleMappingDataSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMapping'); + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); RoleMappingsLogic.actions.initializeRoleMapping(); - await nextTick(); - expect(setErrorMessage).toHaveBeenCalledWith(ROLE_MAPPING_NOT_FOUND); + expect(setRoleMappingDataSpy).not.toHaveBeenCalledWith(asRoleMapping); }); }); @@ -362,7 +322,7 @@ describe('RoleMappingsLogic', () => { 'initializeRoleMappings' ); - http.post.mockReturnValue(Promise.resolve(mappingServerProps)); + http.post.mockReturnValue(Promise.resolve(mappingsServerProps)); RoleMappingsLogic.actions.handleSaveMapping(); expect(http.post).toHaveBeenCalledWith('/api/app_search/role_mappings', { @@ -374,13 +334,16 @@ describe('RoleMappingsLogic', () => { }); it('calls API and refreshes list when existing mapping', async () => { - mount(mappingServerProps); + mount({ + ...mappingsServerProps, + roleMapping: asRoleMapping, + }); const initializeRoleMappingsSpy = jest.spyOn( RoleMappingsLogic.actions, 'initializeRoleMappings' ); - http.put.mockReturnValue(Promise.resolve(mappingServerProps)); + http.put.mockReturnValue(Promise.resolve(mappingsServerProps)); RoleMappingsLogic.actions.handleSaveMapping(); expect(http.put).toHaveBeenCalledWith(`/api/app_search/role_mappings/${asRoleMapping.id}`, { @@ -396,12 +359,13 @@ describe('RoleMappingsLogic', () => { const engine = engines[0]; mount({ - ...mappingServerProps, + ...mappingsServerProps, accessAllEngines: false, selectedEngines: new Set([engine.name]), + roleMapping: asRoleMapping, }); - http.put.mockReturnValue(Promise.resolve(mappingServerProps)); + http.put.mockReturnValue(Promise.resolve(mappingsServerProps)); RoleMappingsLogic.actions.handleSaveMapping(); expect(http.put).toHaveBeenCalledWith(`/api/app_search/role_mappings/${asRoleMapping.id}`, { @@ -449,7 +413,7 @@ describe('RoleMappingsLogic', () => { }); it('calls API and refreshes list', async () => { - mount(mappingServerProps); + mount(mappingsServerProps); const initializeRoleMappingsSpy = jest.spyOn( RoleMappingsLogic.actions, 'initializeRoleMappings' @@ -465,7 +429,7 @@ describe('RoleMappingsLogic', () => { }); it('handles error', async () => { - mount(mappingServerProps); + mount(mappingsServerProps); http.delete.mockReturnValue(Promise.reject('this is an error')); RoleMappingsLogic.actions.handleDeleteMapping(roleMappingId); await nextTick(); @@ -474,7 +438,7 @@ describe('RoleMappingsLogic', () => { }); it('will do nothing if not confirmed', () => { - mount(mappingServerProps); + mount(mappingsServerProps); jest.spyOn(window, 'confirm').mockReturnValueOnce(false); RoleMappingsLogic.actions.handleDeleteMapping(roleMappingId); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts index 863e6746dbe95..fc0a235b23c77 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts @@ -13,10 +13,9 @@ import { clearFlashMessages, flashAPIErrors, setSuccessMessage, - setErrorMessage, } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; -import { ANY_AUTH_PROVIDER, ROLE_MAPPING_NOT_FOUND } from '../../../shared/role_mapping/constants'; +import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; import { AttributeName } from '../../../shared/types'; import { ASRoleMapping, RoleTypes } from '../../types'; import { roleHasScopedEngines } from '../../utils/role/has_scoped_engines'; @@ -31,17 +30,12 @@ import { interface RoleMappingsServerDetails { roleMappings: ASRoleMapping[]; - multipleAuthProvidersConfig: boolean; -} - -interface RoleMappingServerDetails { attributes: string[]; authProviders: string[]; availableEngines: Engine[]; elasticsearchRoles: string[]; hasAdvancedRoles: boolean; multipleAuthProvidersConfig: boolean; - roleMapping?: ASRoleMapping; } const getFirstAttributeName = (roleMapping: ASRoleMapping) => @@ -64,7 +58,7 @@ interface RoleMappingsActions { initializeRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeRoleMappings(): void; resetState(): void; - setRoleMappingData(data: RoleMappingServerDetails): RoleMappingServerDetails; + setRoleMapping(roleMapping: ASRoleMapping): { roleMapping: ASRoleMapping }; setRoleMappingsData(data: RoleMappingsServerDetails): RoleMappingsServerDetails; openRoleMappingFlyout(): void; closeRoleMappingFlyout(): void; @@ -96,7 +90,7 @@ export const RoleMappingsLogic = kea data, - setRoleMappingData: (data: RoleMappingServerDetails) => data, + setRoleMapping: (roleMapping: ASRoleMapping) => ({ roleMapping }), setRoleMappingErrors: (errors: string[]) => ({ errors }), handleAuthProviderChange: (value: string) => ({ value }), handleRoleChange: (roleType: RoleTypes) => ({ roleType }), @@ -120,7 +114,6 @@ export const RoleMappingsLogic = kea false, - setRoleMappingData: () => false, resetState: () => true, }, ], @@ -135,40 +128,39 @@ export const RoleMappingsLogic = kea multipleAuthProvidersConfig, - setRoleMappingData: (_, { multipleAuthProvidersConfig }) => multipleAuthProvidersConfig, resetState: () => false, }, ], hasAdvancedRoles: [ false, { - setRoleMappingData: (_, { hasAdvancedRoles }) => hasAdvancedRoles, + setRoleMappingsData: (_, { hasAdvancedRoles }) => hasAdvancedRoles, }, ], availableEngines: [ [], { - setRoleMappingData: (_, { availableEngines }) => availableEngines, + setRoleMappingsData: (_, { availableEngines }) => availableEngines, resetState: () => [], }, ], attributes: [ [], { - setRoleMappingData: (_, { attributes }) => attributes, + setRoleMappingsData: (_, { attributes }) => attributes, resetState: () => [], }, ], elasticsearchRoles: [ [], { - setRoleMappingData: (_, { elasticsearchRoles }) => elasticsearchRoles, + setRoleMappingsData: (_, { elasticsearchRoles }) => elasticsearchRoles, }, ], roleMapping: [ null, { - setRoleMappingData: (_, { roleMapping }) => roleMapping || null, + setRoleMapping: (_, { roleMapping }) => roleMapping, resetState: () => null, closeRoleMappingFlyout: () => null, }, @@ -176,16 +168,14 @@ export const RoleMappingsLogic = kea - roleMapping ? (roleMapping.roleType as RoleTypes) : 'owner', + setRoleMapping: (_, { roleMapping }) => roleMapping.roleType as RoleTypes, handleRoleChange: (_, { roleType }) => roleType, }, ], accessAllEngines: [ true, { - setRoleMappingData: (_, { roleMapping }) => - roleMapping ? roleMapping.accessAllEngines : true, + setRoleMapping: (_, { roleMapping }) => roleMapping.accessAllEngines, handleRoleChange: (_, { roleType }) => !roleHasScopedEngines(roleType), handleAccessAllEnginesChange: (_, { selected }) => selected, }, @@ -193,8 +183,7 @@ export const RoleMappingsLogic = kea - roleMapping ? getFirstAttributeValue(roleMapping) : '', + setRoleMapping: (_, { roleMapping }) => getFirstAttributeValue(roleMapping), handleAttributeSelectorChange: (_, { value, firstElasticsearchRole }) => value === 'role' ? firstElasticsearchRole : '', handleAttributeValueChange: (_, { value }) => value, @@ -205,8 +194,7 @@ export const RoleMappingsLogic = kea - roleMapping ? getFirstAttributeName(roleMapping) : 'username', + setRoleMapping: (_, { roleMapping }) => getFirstAttributeName(roleMapping), handleAttributeSelectorChange: (_, { value }) => value, resetState: () => 'username', closeRoleMappingFlyout: () => 'username', @@ -215,8 +203,8 @@ export const RoleMappingsLogic = kea - roleMapping ? new Set(roleMapping.engines.map((engine) => engine.name)) : new Set(), + setRoleMapping: (_, { roleMapping }) => + new Set(roleMapping.engines.map((engine: Engine) => engine.name)), handleAccessAllEnginesChange: () => new Set(), handleEngineSelectionChange: (_, { engineNames }) => { const newSelectedEngineNames = new Set() as Set; @@ -229,7 +217,7 @@ export const RoleMappingsLogic = kea authProviders, + setRoleMappingsData: (_, { authProviders }) => authProviders, }, ], selectedAuthProviders: [ @@ -246,8 +234,7 @@ export const RoleMappingsLogic = kea v !== ANY_AUTH_PROVIDER); return [ANY_AUTH_PROVIDER]; }, - setRoleMappingData: (_, { roleMapping }) => - roleMapping ? roleMapping.authProvider : [ANY_AUTH_PROVIDER], + setRoleMapping: (_, { roleMapping }) => roleMapping.authProvider, }, ], roleMappingFlyoutOpen: [ @@ -292,21 +279,8 @@ export const RoleMappingsLogic = kea { - const { http } = HttpLogic.values; - const route = roleMappingId - ? `/api/app_search/role_mappings/${roleMappingId}` - : '/api/app_search/role_mappings/new'; - - try { - const response = await http.get(route); - actions.setRoleMappingData(response); - } catch (e) { - if (e.status === 404) { - setErrorMessage(ROLE_MAPPING_NOT_FOUND); - } else { - flashAPIErrors(e); - } - } + const roleMapping = values.roleMappings.find(({ id }) => id === roleMappingId); + if (roleMapping) actions.setRoleMapping(roleMapping); }, handleDeleteMapping: async ({ roleMappingId }) => { const { http } = HttpLogic.values; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx index 82284be0907fb..7696cf03ed4b1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx @@ -7,7 +7,7 @@ import React, { Fragment } from 'react'; -import { EuiTextColor, EuiInMemoryTable, EuiBasicTableColumn } from '@elastic/eui'; +import { EuiIconTip, EuiTextColor, EuiInMemoryTable, EuiBasicTableColumn } from '@elastic/eui'; import { ASRoleMapping } from '../../app_search/types'; import { WSRoleMapping } from '../../workplace_search/types'; @@ -84,7 +84,7 @@ export const RoleMappingsTable: React.FC = ({ const roleCol: EuiBasicTableColumn = { field: 'roleType', name: ROLE_LABEL, - render: (_, { rules }: SharedRoleMapping) => getFirstAttributeValue(rules), + render: (_, { roleType }: SharedRoleMapping) => roleType, }; const accessItemsCol: EuiBasicTableColumn = { @@ -124,11 +124,16 @@ export const RoleMappingsTable: React.FC = ({ field: 'id', name: '', align: 'right', - render: (_, { id }: SharedRoleMapping) => ( - initializeRoleMapping(id)} - onDeleteClick={() => handleDeleteMapping(id)} - /> + render: (_, { id, toolTip }: SharedRoleMapping) => ( + <> + {id && ( + initializeRoleMapping(id)} + onDeleteClick={() => handleDeleteMapping(id)} + /> + )} + {toolTip && } + ), }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts index b9a49c416f283..e1c2a3b76e3ff 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts @@ -8,3 +8,5 @@ export { WorkplaceSearchNav } from './nav'; export { WorkplaceSearchHeaderActions } from './kibana_header_actions'; export { AccountHeader } from './account_header'; +export { PersonalDashboardLayout } from './personal_dashboard_layout'; +export { PrivateSourcesSidebar, AccountSettingsSidebar } from './personal_dashboard_sidebar'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/index.ts new file mode 100644 index 0000000000000..40347aaee747d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { PersonalDashboardLayout } from './personal_dashboard_layout'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss new file mode 100644 index 0000000000000..175f6b9ebca20 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.scss @@ -0,0 +1,23 @@ +/* + * 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. + */ + +.personalDashboardLayout { + $sideBarWidth: $euiSize * 30; + $consoleHeaderHeight: 48px; // NOTE: Keep an eye on this for changes + $pageHeight: calc(100vh - #{$consoleHeaderHeight}); + + left: $sideBarWidth; + width: calc(100% - #{$sideBarWidth}); + min-height: $pageHeight; + + &__sideBar { + padding: 32px 40px 40px; + width: $sideBarWidth; + margin-left: -$sideBarWidth; + height: $pageHeight; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.test.tsx new file mode 100644 index 0000000000000..faeaa7323e93f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.test.tsx @@ -0,0 +1,41 @@ +/* + * 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 { shallow } from 'enzyme'; + +import { EuiCallOut } from '@elastic/eui'; + +import { AccountHeader } from '..'; + +import { PersonalDashboardLayout } from './personal_dashboard_layout'; + +describe('PersonalDashboardLayout', () => { + const children =

test

; + const sidebar =

test

; + + it('renders', () => { + const wrapper = shallow( + {children} + ); + + expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="TestSidebar"]')).toHaveLength(1); + expect(wrapper.find(AccountHeader)).toHaveLength(1); + }); + + it('renders callout when in read-only mode', () => { + const wrapper = shallow( + + {children} + + ); + + expect(wrapper.find(EuiCallOut)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx new file mode 100644 index 0000000000000..1ab9e07dfa14d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_layout/personal_dashboard_layout.tsx @@ -0,0 +1,51 @@ +/* + * 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 { EuiPage, EuiPageSideBar, EuiPageBody, EuiCallOut } from '@elastic/eui'; + +import { AccountHeader } from '..'; + +import { PRIVATE_DASHBOARD_READ_ONLY_MODE_WARNING } from '../../../views/content_sources/constants'; + +import './personal_dashboard_layout.scss'; + +interface LayoutProps { + restrictWidth?: boolean; + readOnlyMode?: boolean; + sidebar: React.ReactNode; +} + +export const PersonalDashboardLayout: React.FC = ({ + children, + restrictWidth, + readOnlyMode, + sidebar, +}) => { + return ( + <> + + + + {sidebar} + + + {readOnlyMode && ( + + )} + {children} + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/account_settings_sidebar.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/account_settings_sidebar.test.tsx new file mode 100644 index 0000000000000..8edcf83a57e6f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/account_settings_sidebar.test.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { ViewContentHeader } from '../../shared/view_content_header'; + +import { AccountSettingsSidebar } from './account_settings_sidebar'; + +describe('AccountSettingsSidebar', () => { + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(ViewContentHeader)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/account_settings_sidebar.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/account_settings_sidebar.tsx new file mode 100644 index 0000000000000..490f3ff0ae4a5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/account_settings_sidebar.tsx @@ -0,0 +1,17 @@ +/* + * 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 { ACCOUNT_SETTINGS_TITLE, ACCOUNT_SETTINGS_DESCRIPTION } from '../../../constants'; +import { ViewContentHeader } from '../../shared/view_content_header'; + +export const AccountSettingsSidebar = () => { + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/index.ts new file mode 100644 index 0000000000000..ffd241000c8a0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export { PrivateSourcesSidebar } from './private_sources_sidebar'; +export { AccountSettingsSidebar } from './account_settings_sidebar'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.test.tsx similarity index 54% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.test.tsx index 6af439814b891..387724af970f8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.test.tsx @@ -5,50 +5,42 @@ * 2.0. */ -import '../../../__mocks__/shallow_useeffect.mock'; - -import { setMockValues } from '../../../__mocks__/kea_logic'; +import { setMockValues } from '../../../../__mocks__/kea_logic'; import React from 'react'; import { shallow } from 'enzyme'; -import { EuiCallOut } from '@elastic/eui'; - -import { AccountHeader } from '../../components/layout'; -import { ViewContentHeader } from '../../components/shared/view_content_header'; - -import { SourceSubNav } from './components/source_sub_nav'; - import { PRIVATE_CAN_CREATE_PAGE_TITLE, PRIVATE_VIEW_ONLY_PAGE_TITLE, PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION, PRIVATE_CAN_CREATE_PAGE_DESCRIPTION, -} from './constants'; -import { PrivateSourcesLayout } from './private_sources_layout'; +} from '../../../constants'; +import { SourceSubNav } from '../../../views/content_sources/components/source_sub_nav'; + +import { ViewContentHeader } from '../../shared/view_content_header'; -describe('PrivateSourcesLayout', () => { +import { PrivateSourcesSidebar } from './private_sources_sidebar'; + +describe('PrivateSourcesSidebar', () => { const mockValues = { account: { canCreatePersonalSources: true }, }; - const children =

test

; - beforeEach(() => { setMockValues({ ...mockValues }); }); it('renders', () => { - const wrapper = shallow({children}); + const wrapper = shallow(); - expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1); + expect(wrapper.find(ViewContentHeader)).toHaveLength(1); expect(wrapper.find(SourceSubNav)).toHaveLength(1); - expect(wrapper.find(AccountHeader)).toHaveLength(1); }); it('uses correct title and description when private sources are enabled', () => { - const wrapper = shallow({children}); + const wrapper = shallow(); expect(wrapper.find(ViewContentHeader).prop('title')).toEqual(PRIVATE_CAN_CREATE_PAGE_TITLE); expect(wrapper.find(ViewContentHeader).prop('description')).toEqual( @@ -58,17 +50,11 @@ describe('PrivateSourcesLayout', () => { it('uses correct title and description when private sources are disabled', () => { setMockValues({ account: { canCreatePersonalSources: false } }); - const wrapper = shallow({children}); + const wrapper = shallow(); expect(wrapper.find(ViewContentHeader).prop('title')).toEqual(PRIVATE_VIEW_ONLY_PAGE_TITLE); expect(wrapper.find(ViewContentHeader).prop('description')).toEqual( PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION ); }); - - it('renders callout when in read-only mode', () => { - const wrapper = shallow({children}); - - expect(wrapper.find(EuiCallOut)).toHaveLength(1); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.tsx new file mode 100644 index 0000000000000..5505ae57b2ad5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/personal_dashboard_sidebar/private_sources_sidebar.tsx @@ -0,0 +1,40 @@ +/* + * 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 { useValues } from 'kea'; + +import { AppLogic } from '../../../app_logic'; +import { + PRIVATE_CAN_CREATE_PAGE_TITLE, + PRIVATE_VIEW_ONLY_PAGE_TITLE, + PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION, + PRIVATE_CAN_CREATE_PAGE_DESCRIPTION, +} from '../../../constants'; +import { SourceSubNav } from '../../../views/content_sources/components/source_sub_nav'; +import { ViewContentHeader } from '../../shared/view_content_header'; + +export const PrivateSourcesSidebar = () => { + const { + account: { canCreatePersonalSources }, + } = useValues(AppLogic); + + const PAGE_TITLE = canCreatePersonalSources + ? PRIVATE_CAN_CREATE_PAGE_TITLE + : PRIVATE_VIEW_ONLY_PAGE_TITLE; + const PAGE_DESCRIPTION = canCreatePersonalSources + ? PRIVATE_CAN_CREATE_PAGE_DESCRIPTION + : PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION; + + return ( + <> + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts index dcebc35d45f71..aa5419f12c7f3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts @@ -662,6 +662,49 @@ export const PRIVATE_SOURCES = i18n.translate( } ); +export const PRIVATE_CAN_CREATE_PAGE_TITLE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.private.canCreate.title', + { + defaultMessage: 'Manage private content sources', + } +); + +export const PRIVATE_VIEW_ONLY_PAGE_TITLE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.private.vewOnly.title', + { + defaultMessage: 'Review Group Sources', + } +); + +export const PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.private.vewOnly.description', + { + defaultMessage: 'Review the status of all sources shared with your Group.', + } +); + +export const PRIVATE_CAN_CREATE_PAGE_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.private.canCreate.description', + { + defaultMessage: + 'Review the status of all connected private sources, and manage private sources for your account.', + } +); + +export const ACCOUNT_SETTINGS_TITLE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.accountSettings.title', + { + defaultMessage: 'Account Settings', + } +); + +export const ACCOUNT_SETTINGS_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.accountSettings.description', + { + defaultMessage: 'Manage access, passwords, and other account settings.', + } +); + export const CONFIRM_CHANGES_TEXT = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.confirmChanges.text', { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx index 0fc8a6e7c7c0d..7e911b31c516b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx @@ -19,6 +19,11 @@ import { NotFound } from '../shared/not_found'; import { AppLogic } from './app_logic'; import { WorkplaceSearchNav, WorkplaceSearchHeaderActions } from './components/layout'; +import { + PersonalDashboardLayout, + PrivateSourcesSidebar, + AccountSettingsSidebar, +} from './components/layout'; import { GROUPS_PATH, SETUP_GUIDE_PATH, @@ -34,7 +39,6 @@ import { AccountSettings } from './views/account_settings'; import { SourcesRouter } from './views/content_sources'; import { SourceAdded } from './views/content_sources/components/source_added'; import { SourceSubNav } from './views/content_sources/components/source_sub_nav'; -import { PrivateSourcesLayout } from './views/content_sources/private_sources_layout'; import { ErrorState } from './views/error_state'; import { GroupsRouter } from './views/groups'; import { GroupSubNav } from './views/groups/components/group_sub_nav'; @@ -101,14 +105,22 @@ export const WorkplaceSearchConfigured: React.FC = (props) => { )} - + } + > - + - + } + > - + = ({ - children, - restrictWidth, - readOnlyMode, -}) => { - const { - account: { canCreatePersonalSources }, - } = useValues(AppLogic); - - const PAGE_TITLE = canCreatePersonalSources - ? PRIVATE_CAN_CREATE_PAGE_TITLE - : PRIVATE_VIEW_ONLY_PAGE_TITLE; - const PAGE_DESCRIPTION = canCreatePersonalSources - ? PRIVATE_CAN_CREATE_PAGE_DESCRIPTION - : PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION; - - return ( - <> - - - - - - - - {readOnlyMode && ( - - )} - {children} - - - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss index 549ca3ae9154e..feccc0e1924d2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss @@ -18,23 +18,6 @@ } } -.privateSourcesLayout { - $sideBarWidth: $euiSize * 30; - $consoleHeaderHeight: 48px; // NOTE: Keep an eye on this for changes - $pageHeight: calc(100vh - #{$consoleHeaderHeight}); - - left: $sideBarWidth; - width: calc(100% - #{$sideBarWidth}); - min-height: $pageHeight; - - &__sideBar { - padding: 32px 40px 40px; - width: $sideBarWidth; - margin-left: -$sideBarWidth; - height: $pageHeight; - } -} - .sourcesSubNav { li { display: block; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts index 716cb8ebb6d47..4ee530870284e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts @@ -16,13 +16,13 @@ import { groups } from '../../__mocks__/groups.mock'; import { nextTick } from '@kbn/test/jest'; import { wsRoleMapping } from '../../../shared/role_mapping/__mocks__/roles'; -import { ANY_AUTH_PROVIDER, ROLE_MAPPING_NOT_FOUND } from '../../../shared/role_mapping/constants'; +import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; import { RoleMappingsLogic } from './role_mappings_logic'; describe('RoleMappingsLogic', () => { const { http } = mockHttpValues; - const { clearFlashMessages, flashAPIErrors, setErrorMessage } = mockFlashMessageHelpers; + const { clearFlashMessages, flashAPIErrors } = mockFlashMessageHelpers; const { mount } = new LogicMounter(RoleMappingsLogic); const defaultValues = { attributes: [], @@ -52,14 +52,13 @@ describe('RoleMappingsLogic', () => { name: 'Default', }; - const mappingsServerProps = { multipleAuthProvidersConfig: true, roleMappings: [wsRoleMapping] }; - const mappingServerProps = { + const mappingsServerProps = { + multipleAuthProvidersConfig: true, + roleMappings: [wsRoleMapping], attributes: [], authProviders: [], availableGroups: [roleGroup, defaultGroup], elasticsearchRoles: [], - multipleAuthProvidersConfig: false, - roleMapping: wsRoleMapping, }; beforeEach(() => { @@ -78,36 +77,17 @@ describe('RoleMappingsLogic', () => { expect(RoleMappingsLogic.values.roleMappings).toEqual([wsRoleMapping]); expect(RoleMappingsLogic.values.dataLoading).toEqual(false); expect(RoleMappingsLogic.values.multipleAuthProvidersConfig).toEqual(true); - }); - - describe('setRoleMappingData', () => { - it('sets data correctly', () => { - RoleMappingsLogic.actions.setRoleMappingData(mappingServerProps); - - expect(RoleMappingsLogic.values.roleMapping).toEqual(wsRoleMapping); - expect(RoleMappingsLogic.values.dataLoading).toEqual(false); - expect(RoleMappingsLogic.values.attributes).toEqual(mappingServerProps.attributes); - expect(RoleMappingsLogic.values.availableGroups).toEqual( - mappingServerProps.availableGroups - ); - expect(RoleMappingsLogic.values.includeInAllGroups).toEqual(true); - expect(RoleMappingsLogic.values.elasticsearchRoles).toEqual( - mappingServerProps.elasticsearchRoles - ); - expect(RoleMappingsLogic.values.selectedGroups).toEqual( - new Set([wsRoleMapping.groups[0].id]) - ); - expect(RoleMappingsLogic.values.selectedOptions).toEqual([]); - }); - - it('sets default group with new role mapping', () => { - RoleMappingsLogic.actions.setRoleMappingData({ - ...mappingServerProps, - roleMapping: undefined, - }); - - expect(RoleMappingsLogic.values.selectedGroups).toEqual(new Set([defaultGroup.id])); - }); + expect(RoleMappingsLogic.values.dataLoading).toEqual(false); + expect(RoleMappingsLogic.values.attributes).toEqual(mappingsServerProps.attributes); + expect(RoleMappingsLogic.values.availableGroups).toEqual(mappingsServerProps.availableGroups); + expect(RoleMappingsLogic.values.includeInAllGroups).toEqual(false); + expect(RoleMappingsLogic.values.elasticsearchRoles).toEqual( + mappingsServerProps.elasticsearchRoles + ); + expect(RoleMappingsLogic.values.selectedOptions).toEqual([ + { label: defaultGroup.name, value: defaultGroup.id }, + ]); + expect(RoleMappingsLogic.values.selectedGroups).toEqual(new Set([defaultGroup.id])); }); it('handleRoleChange', () => { @@ -119,14 +99,17 @@ describe('RoleMappingsLogic', () => { it('handleGroupSelectionChange', () => { const group = wsRoleMapping.groups[0]; const otherGroup = groups[0]; - RoleMappingsLogic.actions.setRoleMappingData({ - ...mappingServerProps, - roleMapping: { - ...wsRoleMapping, - groups: [group, otherGroup], - }, + RoleMappingsLogic.actions.setRoleMappingsData({ + ...mappingsServerProps, + roleMappings: [ + { + ...wsRoleMapping, + groups: [group, otherGroup], + }, + ], }); + RoleMappingsLogic.actions.initializeRoleMapping(wsRoleMapping.id); RoleMappingsLogic.actions.handleGroupSelectionChange([group.id, otherGroup.id]); expect(RoleMappingsLogic.values.selectedGroups).toEqual(new Set([group.id, otherGroup.id])); expect(RoleMappingsLogic.values.selectedOptions).toEqual([ @@ -147,8 +130,8 @@ describe('RoleMappingsLogic', () => { const elasticsearchRoles = ['foo', 'bar']; it('sets values correctly', () => { - RoleMappingsLogic.actions.setRoleMappingData({ - ...mappingServerProps, + RoleMappingsLogic.actions.setRoleMappingsData({ + ...mappingsServerProps, elasticsearchRoles, }); RoleMappingsLogic.actions.handleAttributeSelectorChange('role', elasticsearchRoles[0]); @@ -172,12 +155,14 @@ describe('RoleMappingsLogic', () => { describe('handleAuthProviderChange', () => { beforeEach(() => { - RoleMappingsLogic.actions.setRoleMappingData({ - ...mappingServerProps, - roleMapping: { - ...wsRoleMapping, - authProvider: ['foo'], - }, + RoleMappingsLogic.actions.setRoleMappingsData({ + ...mappingsServerProps, + roleMappings: [ + { + ...wsRoleMapping, + authProvider: ['foo'], + }, + ], }); }); const providers = ['bar', 'baz']; @@ -201,28 +186,23 @@ describe('RoleMappingsLogic', () => { }); it('handles "any" auth in previous state', () => { - RoleMappingsLogic.actions.setRoleMappingData({ - ...mappingServerProps, - roleMapping: { - ...wsRoleMapping, - authProvider: [ANY_AUTH_PROVIDER], - }, + RoleMappingsLogic.actions.setRoleMappingsData({ + ...mappingsServerProps, + roleMappings: [ + { + ...wsRoleMapping, + authProvider: [ANY_AUTH_PROVIDER], + }, + ], }); RoleMappingsLogic.actions.handleAuthProviderChange(providerWithAny); expect(RoleMappingsLogic.values.selectedAuthProviders).toEqual([providers[1]]); }); - - it('handles catch-all state', () => { - RoleMappingsLogic.actions.handleAuthProviderChange(providerWithAny); - - expect(RoleMappingsLogic.values.selectedAuthProviders).toEqual([ANY_AUTH_PROVIDER]); - }); }); it('resetState', () => { RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); - RoleMappingsLogic.actions.setRoleMappingData(mappingServerProps); RoleMappingsLogic.actions.resetState(); expect(RoleMappingsLogic.values.dataLoading).toEqual(true); @@ -234,7 +214,7 @@ describe('RoleMappingsLogic', () => { }); it('openRoleMappingFlyout', () => { - mount(mappingServerProps); + mount(mappingsServerProps); RoleMappingsLogic.actions.openRoleMappingFlyout(); expect(RoleMappingsLogic.values.roleMappingFlyoutOpen).toEqual(true); @@ -243,7 +223,7 @@ describe('RoleMappingsLogic', () => { it('closeRoleMappingFlyout', () => { mount({ - ...mappingServerProps, + ...mappingsServerProps, roleMappingFlyoutOpen: true, }); RoleMappingsLogic.actions.closeRoleMappingFlyout(); @@ -275,40 +255,20 @@ describe('RoleMappingsLogic', () => { }); describe('initializeRoleMapping', () => { - it('calls API and sets values for new mapping', async () => { - const setRoleMappingDataSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMappingData'); - http.get.mockReturnValue(Promise.resolve(mappingServerProps)); - RoleMappingsLogic.actions.initializeRoleMapping(); - - expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/role_mappings/new'); - await nextTick(); - expect(setRoleMappingDataSpy).toHaveBeenCalledWith(mappingServerProps); - }); - - it('calls API and sets values for existing mapping', async () => { - const setRoleMappingDataSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMappingData'); - http.get.mockReturnValue(Promise.resolve(mappingServerProps)); - RoleMappingsLogic.actions.initializeRoleMapping('123'); - - expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/role_mappings/123'); - await nextTick(); - expect(setRoleMappingDataSpy).toHaveBeenCalledWith(mappingServerProps); - }); - - it('handles error', async () => { - http.get.mockReturnValue(Promise.reject('this is an error')); - RoleMappingsLogic.actions.initializeRoleMapping(); - await nextTick(); + it('sets values for existing mapping', () => { + const setRoleMappingDataSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMapping'); + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + RoleMappingsLogic.actions.initializeRoleMapping(wsRoleMapping.id); - expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + expect(setRoleMappingDataSpy).toHaveBeenCalledWith(wsRoleMapping); }); - it('shows error when there is a 404 status', async () => { - http.get.mockReturnValue(Promise.reject({ status: 404 })); + it('does not set data for new mapping', async () => { + const setRoleMappingDataSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMapping'); + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); RoleMappingsLogic.actions.initializeRoleMapping(); - await nextTick(); - expect(setErrorMessage).toHaveBeenCalledWith(ROLE_MAPPING_NOT_FOUND); + expect(setRoleMappingDataSpy).not.toHaveBeenCalledWith(wsRoleMapping); }); }); @@ -320,7 +280,7 @@ describe('RoleMappingsLogic', () => { ); RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); - http.post.mockReturnValue(Promise.resolve(mappingServerProps)); + http.post.mockReturnValue(Promise.resolve(mappingsServerProps)); RoleMappingsLogic.actions.handleSaveMapping(); expect(http.post).toHaveBeenCalledWith('/api/workplace_search/org/role_mappings', { @@ -331,7 +291,7 @@ describe('RoleMappingsLogic', () => { rules: { username: '', }, - groups: [], + groups: [defaultGroup.id], }), }); await nextTick(); @@ -344,9 +304,9 @@ describe('RoleMappingsLogic', () => { RoleMappingsLogic.actions, 'initializeRoleMappings' ); - RoleMappingsLogic.actions.setRoleMappingData(mappingServerProps); + RoleMappingsLogic.actions.setRoleMapping(wsRoleMapping); - http.put.mockReturnValue(Promise.resolve(mappingServerProps)); + http.put.mockReturnValue(Promise.resolve(mappingsServerProps)); RoleMappingsLogic.actions.handleSaveMapping(); expect(http.put).toHaveBeenCalledWith( @@ -408,7 +368,7 @@ describe('RoleMappingsLogic', () => { RoleMappingsLogic.actions, 'initializeRoleMappings' ); - RoleMappingsLogic.actions.setRoleMappingData(mappingServerProps); + RoleMappingsLogic.actions.setRoleMapping(wsRoleMapping); http.delete.mockReturnValue(Promise.resolve({})); RoleMappingsLogic.actions.handleDeleteMapping(roleMappingId); @@ -421,7 +381,7 @@ describe('RoleMappingsLogic', () => { }); it('handles error', async () => { - RoleMappingsLogic.actions.setRoleMappingData(mappingServerProps); + RoleMappingsLogic.actions.setRoleMapping(wsRoleMapping); http.delete.mockReturnValue(Promise.reject('this is an error')); RoleMappingsLogic.actions.handleDeleteMapping(roleMappingId); await nextTick(); @@ -430,7 +390,7 @@ describe('RoleMappingsLogic', () => { }); it('will do nothing if not confirmed', async () => { - RoleMappingsLogic.actions.setRoleMappingData(mappingServerProps); + RoleMappingsLogic.actions.setRoleMapping(wsRoleMapping); window.confirm = () => false; RoleMappingsLogic.actions.handleDeleteMapping(roleMappingId); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index aee780ac18971..361425b7a78a1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -13,10 +13,9 @@ import { clearFlashMessages, flashAPIErrors, setSuccessMessage, - setErrorMessage, } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; -import { ANY_AUTH_PROVIDER, ROLE_MAPPING_NOT_FOUND } from '../../../shared/role_mapping/constants'; +import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; import { AttributeName } from '../../../shared/types'; import { RoleGroup, WSRoleMapping, Role } from '../../types'; @@ -30,16 +29,11 @@ import { interface RoleMappingsServerDetails { roleMappings: WSRoleMapping[]; - multipleAuthProvidersConfig: boolean; -} - -interface RoleMappingServerDetails { attributes: string[]; authProviders: string[]; availableGroups: RoleGroup[]; elasticsearchRoles: string[]; multipleAuthProvidersConfig: boolean; - roleMapping?: WSRoleMapping; } const getFirstAttributeName = (roleMapping: WSRoleMapping): AttributeName => @@ -62,7 +56,7 @@ interface RoleMappingsActions { initializeRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeRoleMappings(): void; resetState(): void; - setRoleMappingData(data: RoleMappingServerDetails): RoleMappingServerDetails; + setRoleMapping(roleMapping: WSRoleMapping): { roleMapping: WSRoleMapping }; setRoleMappingsData(data: RoleMappingsServerDetails): RoleMappingsServerDetails; openRoleMappingFlyout(): void; closeRoleMappingFlyout(): void; @@ -93,7 +87,7 @@ export const RoleMappingsLogic = kea data, - setRoleMappingData: (data: RoleMappingServerDetails) => data, + setRoleMapping: (roleMapping: WSRoleMapping) => ({ roleMapping }), setRoleMappingErrors: (errors: string[]) => ({ errors }), handleAuthProviderChange: (value: string[]) => ({ value }), handleRoleChange: (roleType: Role) => ({ roleType }), @@ -117,7 +111,6 @@ export const RoleMappingsLogic = kea false, - setRoleMappingData: () => false, resetState: () => true, }, ], @@ -132,32 +125,31 @@ export const RoleMappingsLogic = kea multipleAuthProvidersConfig, - setRoleMappingData: (_, { multipleAuthProvidersConfig }) => multipleAuthProvidersConfig, resetState: () => false, }, ], availableGroups: [ [], { - setRoleMappingData: (_, { availableGroups }) => availableGroups, + setRoleMappingsData: (_, { availableGroups }) => availableGroups, }, ], attributes: [ [], { - setRoleMappingData: (_, { attributes }) => attributes, + setRoleMappingsData: (_, { attributes }) => attributes, }, ], elasticsearchRoles: [ [], { - setRoleMappingData: (_, { elasticsearchRoles }) => elasticsearchRoles, + setRoleMappingsData: (_, { elasticsearchRoles }) => elasticsearchRoles, }, ], roleMapping: [ null, { - setRoleMappingData: (_, { roleMapping }) => roleMapping || null, + setRoleMapping: (_, { roleMapping }) => roleMapping, resetState: () => null, closeRoleMappingFlyout: () => null, }, @@ -165,23 +157,21 @@ export const RoleMappingsLogic = kea - roleMapping ? (roleMapping.roleType as Role) : 'admin', + setRoleMapping: (_, { roleMapping }) => roleMapping.roleType as Role, handleRoleChange: (_, { roleType }) => roleType, }, ], includeInAllGroups: [ false, { - setRoleMappingData: (_, { roleMapping }) => (roleMapping ? roleMapping.allGroups : false), + setRoleMapping: (_, { roleMapping }) => roleMapping.allGroups, handleAllGroupsSelectionChange: (_, { selected }) => selected, }, ], attributeValue: [ '', { - setRoleMappingData: (_, { roleMapping }) => - roleMapping ? getFirstAttributeValue(roleMapping) : '', + setRoleMapping: (_, { roleMapping }) => getFirstAttributeValue(roleMapping), handleAttributeSelectorChange: (_, { value, firstElasticsearchRole }) => value === 'role' ? firstElasticsearchRole : '', handleAttributeValueChange: (_, { value }) => value, @@ -192,8 +182,7 @@ export const RoleMappingsLogic = kea - roleMapping ? getFirstAttributeName(roleMapping) : 'username', + setRoleMapping: (_, { roleMapping }) => getFirstAttributeName(roleMapping), handleAttributeSelectorChange: (_, { value }) => value, resetState: () => 'username', closeRoleMappingFlyout: () => 'username', @@ -202,14 +191,14 @@ export const RoleMappingsLogic = kea - roleMapping - ? new Set(roleMapping.groups.map((group) => group.id)) - : new Set( - availableGroups - .filter((group) => group.name === DEFAULT_GROUP_NAME) - .map((group) => group.id) - ), + setRoleMappingsData: (_, { availableGroups }) => + new Set( + availableGroups + .filter((group) => group.name === DEFAULT_GROUP_NAME) + .map((group) => group.id) + ), + setRoleMapping: (_, { roleMapping }) => + new Set(roleMapping.groups.map((group: RoleGroup) => group.id)), handleGroupSelectionChange: (_, { groupIds }) => { const newSelectedGroupNames = new Set() as Set; groupIds.forEach((groupId) => newSelectedGroupNames.add(groupId)); @@ -221,7 +210,7 @@ export const RoleMappingsLogic = kea authProviders, + setRoleMappingsData: (_, { authProviders }) => authProviders, }, ], selectedAuthProviders: [ @@ -230,15 +219,15 @@ export const RoleMappingsLogic = kea { const previouslyContainedAny = previous.includes(ANY_AUTH_PROVIDER); const newSelectionsContainAny = value.includes(ANY_AUTH_PROVIDER); + const hasItems = value.length > 0; - if (value.length < 1) return [ANY_AUTH_PROVIDER]; if (value.length === 1) return value; - if (!newSelectionsContainAny) return value; - if (previouslyContainedAny) return value.filter((v) => v !== ANY_AUTH_PROVIDER); + if (!newSelectionsContainAny && hasItems) return value; + if (previouslyContainedAny && hasItems) + return value.filter((v) => v !== ANY_AUTH_PROVIDER); return [ANY_AUTH_PROVIDER]; }, - setRoleMappingData: (_, { roleMapping }) => - roleMapping ? roleMapping.authProvider : [ANY_AUTH_PROVIDER], + setRoleMapping: (_, { roleMapping }) => roleMapping.authProvider, }, ], roleMappingFlyoutOpen: [ @@ -283,21 +272,8 @@ export const RoleMappingsLogic = kea { - const { http } = HttpLogic.values; - const route = roleMappingId - ? `/api/workplace_search/org/role_mappings/${roleMappingId}` - : '/api/workplace_search/org/role_mappings/new'; - - try { - const response = await http.get(route); - actions.setRoleMappingData(response); - } catch (e) { - if (e.status === 404) { - setErrorMessage(ROLE_MAPPING_NOT_FOUND); - } else { - flashAPIErrors(e); - } - } + const roleMapping = values.roleMappings.find(({ id }) => id === roleMappingId); + if (roleMapping) actions.setRoleMapping(roleMapping); }, handleDeleteMapping: async ({ roleMappingId }) => { const { http } = HttpLogic.values; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts index a126d06f303b4..718597c12e9c5 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts @@ -7,11 +7,7 @@ import { MockRouter, mockRequestHandler, mockDependencies } from '../../__mocks__'; -import { - registerRoleMappingsRoute, - registerRoleMappingRoute, - registerNewRoleMappingRoute, -} from './role_mappings'; +import { registerRoleMappingsRoute, registerRoleMappingRoute } from './role_mappings'; const roleMappingBaseSchema = { rules: { username: 'user' }, @@ -80,29 +76,6 @@ describe('role mappings routes', () => { }); }); - describe('GET /api/app_search/role_mappings/{id}', () => { - let mockRouter: MockRouter; - - beforeEach(() => { - jest.clearAllMocks(); - mockRouter = new MockRouter({ - method: 'get', - path: '/api/app_search/role_mappings/{id}', - }); - - registerRoleMappingRoute({ - ...mockDependencies, - router: mockRouter.router, - }); - }); - - it('creates a request handler', () => { - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/role_mappings/:id', - }); - }); - }); - describe('PUT /api/app_search/role_mappings/{id}', () => { let mockRouter: MockRouter; @@ -160,27 +133,4 @@ describe('role mappings routes', () => { }); }); }); - - describe('GET /api/app_search/role_mappings/new', () => { - let mockRouter: MockRouter; - - beforeEach(() => { - jest.clearAllMocks(); - mockRouter = new MockRouter({ - method: 'get', - path: '/api/app_search/role_mappings/new', - }); - - registerNewRoleMappingRoute({ - ...mockDependencies, - router: mockRouter.router, - }); - }); - - it('creates a request handler', () => { - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/role_mappings/new', - }); - }); - }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts index 86e17b575e019..75724a3344d6d 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts @@ -48,20 +48,6 @@ export function registerRoleMappingRoute({ router, enterpriseSearchRequestHandler, }: RouteDependencies) { - router.get( - { - path: '/api/app_search/role_mappings/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - }, - }, - enterpriseSearchRequestHandler.createRequest({ - path: '/role_mappings/:id', - }) - ); - router.put( { path: '/api/app_search/role_mappings/{id}', @@ -92,23 +78,7 @@ export function registerRoleMappingRoute({ ); } -export function registerNewRoleMappingRoute({ - router, - enterpriseSearchRequestHandler, -}: RouteDependencies) { - router.get( - { - path: '/api/app_search/role_mappings/new', - validate: false, - }, - enterpriseSearchRequestHandler.createRequest({ - path: '/role_mappings/new', - }) - ); -} - export const registerRoleMappingsRoutes = (dependencies: RouteDependencies) => { registerRoleMappingsRoute(dependencies); registerRoleMappingRoute(dependencies); - registerNewRoleMappingRoute(dependencies); }; diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts index 0dade134767e4..a945866da5ef2 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts @@ -7,11 +7,7 @@ import { MockRouter, mockRequestHandler, mockDependencies } from '../../__mocks__'; -import { - registerOrgRoleMappingsRoute, - registerOrgRoleMappingRoute, - registerOrgNewRoleMappingRoute, -} from './role_mappings'; +import { registerOrgRoleMappingsRoute, registerOrgRoleMappingRoute } from './role_mappings'; describe('role mappings routes', () => { describe('GET /api/workplace_search/org/role_mappings', () => { @@ -60,29 +56,6 @@ describe('role mappings routes', () => { }); }); - describe('GET /api/workplace_search/org/role_mappings/{id}', () => { - let mockRouter: MockRouter; - - beforeEach(() => { - jest.clearAllMocks(); - mockRouter = new MockRouter({ - method: 'get', - path: '/api/workplace_search/org/role_mappings/{id}', - }); - - registerOrgRoleMappingRoute({ - ...mockDependencies, - router: mockRouter.router, - }); - }); - - it('creates a request handler', () => { - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/role_mappings/:id', - }); - }); - }); - describe('PUT /api/workplace_search/org/role_mappings/{id}', () => { let mockRouter: MockRouter; @@ -128,27 +101,4 @@ describe('role mappings routes', () => { }); }); }); - - describe('GET /api/workplace_search/org/role_mappings/new', () => { - let mockRouter: MockRouter; - - beforeEach(() => { - jest.clearAllMocks(); - mockRouter = new MockRouter({ - method: 'get', - path: '/api/workplace_search/org/role_mappings/new', - }); - - registerOrgNewRoleMappingRoute({ - ...mockDependencies, - router: mockRouter.router, - }); - }); - - it('creates a request handler', () => { - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/role_mappings/new', - }); - }); - }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.ts index 5a6359c1cd836..a0fcec63cbb27 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.ts @@ -48,20 +48,6 @@ export function registerOrgRoleMappingRoute({ router, enterpriseSearchRequestHandler, }: RouteDependencies) { - router.get( - { - path: '/api/workplace_search/org/role_mappings/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - }, - }, - enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/role_mappings/:id', - }) - ); - router.put( { path: '/api/workplace_search/org/role_mappings/{id}', @@ -92,23 +78,7 @@ export function registerOrgRoleMappingRoute({ ); } -export function registerOrgNewRoleMappingRoute({ - router, - enterpriseSearchRequestHandler, -}: RouteDependencies) { - router.get( - { - path: '/api/workplace_search/org/role_mappings/new', - validate: false, - }, - enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/role_mappings/new', - }) - ); -} - export const registerRoleMappingsRoutes = (dependencies: RouteDependencies) => { registerOrgRoleMappingsRoute(dependencies); registerOrgRoleMappingRoute(dependencies); - registerOrgNewRoleMappingRoute(dependencies); }; diff --git a/x-pack/plugins/snapshot_restore/public/application/app.tsx b/x-pack/plugins/snapshot_restore/public/application/app.tsx index 0c064d448105f..a7993300079ab 100644 --- a/x-pack/plugins/snapshot_restore/public/application/app.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/app.tsx @@ -10,14 +10,16 @@ import { Redirect, Route, Switch } from 'react-router-dom'; import { EuiPageContent } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { APP_WRAPPER_CLASS } from '../../../../../src/core/public'; + import { APP_REQUIRED_CLUSTER_PRIVILEGES } from '../../common'; import { useAuthorizationContext, - SectionError, + PageError, WithPrivileges, NotAuthorizedSection, } from '../shared_imports'; -import { SectionLoading } from './components'; +import { PageLoading } from './components'; import { DEFAULT_SECTION, Section } from './constants'; import { RepositoryAdd, @@ -42,7 +44,7 @@ export const App: React.FunctionComponent = () => { const sectionsRegex = sections.join('|'); return apiError ? ( - { `cluster.${name}`)}> {({ isLoading, hasPrivileges, privilegesMissing }) => isLoading ? ( - + - + ) : hasPrivileges ? ( -
+
@@ -84,7 +86,7 @@ export const App: React.FunctionComponent = () => {
) : ( - + = ({ children, ...rest }) => { + return ( + + + + + + + {children} + + + + ); +}; + +export const SectionLoading: React.FunctionComponent = ({ children }) => { + return ( + } + body={{children}} + data-test-subj="sectionLoading" + /> + ); +}; + +/* + * Loading component used for full page loads. + * For tabbed sections, or within the context of a wizard, + * the component may be more appropriate + */ +export const PageLoading: React.FunctionComponent = ({ children }) => { + return ( + + } + body={{children}} + data-test-subj="sectionLoading" + /> + + ); +}; diff --git a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx index 6443d774c9ac7..06c65a1713692 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx @@ -30,7 +30,7 @@ import { useCore, useServices } from '../../../app_context'; import { DEFAULT_POLICY_SCHEDULE, DEFAULT_POLICY_FREQUENCY } from '../../../constants'; import { useLoadRepositories } from '../../../services/http'; import { linkToAddRepository } from '../../../services/navigation'; -import { SectionLoading } from '../../'; +import { InlineLoading } from '../../'; import { StepProps } from './'; import { reactRouterNavigate } from '../../../../../../../../src/plugins/kibana_react/public'; @@ -174,12 +174,12 @@ export const PolicyStepLogistics: React.FunctionComponent = ({ const renderRepositorySelect = () => { if (isLoadingRepositories) { return ( - + - + ); } diff --git a/x-pack/plugins/snapshot_restore/public/application/components/section_loading.tsx b/x-pack/plugins/snapshot_restore/public/application/components/section_loading.tsx deleted file mode 100644 index c1548ad960bb0..0000000000000 --- a/x-pack/plugins/snapshot_restore/public/application/components/section_loading.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 { - EuiEmptyPrompt, - EuiLoadingSpinner, - EuiText, - EuiFlexGroup, - EuiFlexItem, - EuiTextColor, -} from '@elastic/eui'; - -interface Props { - inline?: boolean; - children: React.ReactNode; - [key: string]: any; -} - -export const SectionLoading: React.FunctionComponent = ({ inline, children, ...rest }) => { - if (inline) { - return ( - - - - - - - {children} - - - - ); - } - - return ( - } - body={{children}} - data-test-subj="sectionLoading" - /> - ); -}; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/home.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/home.tsx index e4a23bac636d8..211d30181c25c 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/home.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/home.tsx @@ -9,18 +9,7 @@ import React, { useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { Route, RouteComponentProps, Switch } from 'react-router-dom'; -import { - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiPageBody, - EuiPageContent, - EuiSpacer, - EuiTab, - EuiTabs, - EuiTitle, - EuiText, -} from '@elastic/eui'; +import { EuiButtonEmpty, EuiPageHeader, EuiSpacer } from '@elastic/eui'; import { BASE_PATH, Section } from '../../constants'; import { useConfig, useCore } from '../../app_context'; @@ -100,79 +89,65 @@ export const SnapshotRestoreHome: React.FunctionComponent - - - - -

- -

-
- - - - - -
-
- - - + <> + - - - - - - - {tabs.map((tab) => ( - onSectionChange(tab.id)} - isSelected={tab.id === section} - key={tab.id} - data-test-subj={tab.id.toLowerCase() + '_tab'} - > - {tab.name} - - ))} - + + } + rightSideItems={[ + + + , + ]} + description={ + + } + tabs={tabs.map((tab) => ({ + onClick: () => onSectionChange(tab.id), + isSelected: tab.id === section, + key: tab.id, + 'data-test-subj': tab.id.toLowerCase() + '_tab', + label: tab.name, + }))} + /> - + - - - {/* We have two separate SnapshotList routes because repository names could have slashes in - * them. This would break a route with a path like snapshots/:repositoryName?/:snapshotId* - */} - - - - - -
- + + + {/* We have two separate SnapshotList routes because repository names could have slashes in + * them. This would break a route with a path like snapshots/:repositoryName?/:snapshotId* + */} + + + + + + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx index 2bad30b95081d..0a283d406e5aa 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx @@ -40,6 +40,7 @@ import { linkToEditPolicy, linkToSnapshot } from '../../../../services/navigatio import { SectionLoading, + InlineLoading, PolicyExecuteProvider, PolicyDeleteProvider, } from '../../../../components'; @@ -318,7 +319,7 @@ export const PolicyDetails: React.FunctionComponent = ({ {policyDetails && policyDetails.policy && policyDetails.policy.inProgress ? ( <> - + = ({ values={{ snapshotName: policyDetails.policy.inProgress.snapshotName }} /> - + ) : null} {renderTabs()} diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/policy_add/policy_add.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/policy_add/policy_add.tsx index 7b1c10ec59e8a..3927b73abf093 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/policy_add/policy_add.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/policy_add/policy_add.tsx @@ -9,13 +9,13 @@ import React, { useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { RouteComponentProps } from 'react-router-dom'; -import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { EuiPageContentBody, EuiSpacer, EuiPageHeader } from '@elastic/eui'; import { SlmPolicyPayload } from '../../../../common/types'; import { TIME_UNITS } from '../../../../common'; -import { SectionError, Error } from '../../../shared_imports'; +import { SectionError, PageError } from '../../../shared_imports'; -import { PolicyForm, SectionLoading } from '../../components'; +import { PolicyForm, PageLoading } from '../../components'; import { BASE_PATH, DEFAULT_POLICY_SCHEDULE } from '../../constants'; import { breadcrumbService, docTitleService } from '../../services/navigation'; import { addPolicy, useLoadIndices } from '../../services/http'; @@ -87,49 +87,57 @@ export const PolicyAdd: React.FunctionComponent = ({ setSaveError(null); }; + if (isLoadingIndices) { + return ( + + + + ); + } + + if (errorLoadingIndices) { + return ( + + } + error={errorLoadingIndices} + /> + ); + } + return ( - - - -

+ + -

-
- - {isLoadingIndices ? ( - - - - ) : errorLoadingIndices ? ( - - } - error={errorLoadingIndices as Error} - /> - ) : ( - - )} -
-
+ + } + /> + + + + + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx index 0ad1902845770..4ab0f15cc5523 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx @@ -9,12 +9,12 @@ import React, { useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { RouteComponentProps } from 'react-router-dom'; -import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle, EuiCallOut } from '@elastic/eui'; +import { EuiPageContentBody, EuiPageHeader, EuiSpacer, EuiCallOut } from '@elastic/eui'; import { SlmPolicyPayload } from '../../../../common/types'; -import { SectionError, Error } from '../../../shared_imports'; +import { SectionError, Error, PageError } from '../../../shared_imports'; import { useDecodedParams } from '../../lib'; import { TIME_UNITS } from '../../../../common/constants'; -import { SectionLoading, PolicyForm } from '../../components'; +import { PageLoading, PolicyForm } from '../../components'; import { BASE_PATH } from '../../constants'; import { useServices } from '../../app_context'; import { breadcrumbService, docTitleService } from '../../services/navigation'; @@ -106,21 +106,39 @@ export const PolicyEdit: React.FunctionComponent { + return saveError ? ( + + } + error={saveError} + /> + ) : null; + }; + + const clearSaveError = () => { + setSaveError(null); + }; + const renderLoading = () => { - return errorLoadingPolicy ? ( - + return isLoadingPolicy ? ( + - + ) : ( - + - + ); }; @@ -139,8 +157,9 @@ export const PolicyEdit: React.FunctionComponent - } - error={errorLoadingIndices as Error} - /> - ); - } - }; - - const renderSaveError = () => { - return saveError ? ( - } - error={saveError} + error={errorLoadingIndices as Error} /> - ) : null; - }; - - const clearSaveError = () => { - setSaveError(null); + ); }; - const renderContent = () => { - if (isLoadingPolicy || isLoadingIndices) { - return renderLoading(); - } - if (errorLoadingPolicy || errorLoadingIndices) { - return renderError(); - } + if (isLoadingPolicy || isLoadingIndices) { + return renderLoading(); + } - return ( - <> - {policy.isManagedPolicy ? ( - <> - - } - /> - - - ) : null} - - - ); - }; + if (errorLoadingPolicy || errorLoadingIndices) { + return renderError(); + } return ( - - - -

+ + -

-
- - {renderContent()} -
-
+ + } + /> + + + {policy.isManagedPolicy ? ( + <> + + } + /> + + + ) : null} + + + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/repository_add/repository_add.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/repository_add/repository_add.tsx index 343c0b60a2253..100d345a49c4d 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/repository_add/repository_add.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/repository_add/repository_add.tsx @@ -10,7 +10,7 @@ import React, { useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { RouteComponentProps } from 'react-router-dom'; -import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { EuiPageContentBody, EuiSpacer, EuiPageHeader } from '@elastic/eui'; import { Repository, EmptyRepository } from '../../../../common/types'; import { SectionError } from '../../../shared_imports'; @@ -79,25 +79,27 @@ export const RepositoryAdd: React.FunctionComponent = ({ }; return ( - - - -

+ + -

-
- - -
-
+ + } + /> + + + + + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/repository_edit/repository_edit.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/repository_edit/repository_edit.tsx index e27dd255f3bdf..9ecd1d0e3fafe 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/repository_edit/repository_edit.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/repository_edit/repository_edit.tsx @@ -5,15 +5,15 @@ * 2.0. */ -import React, { useEffect, useState, Fragment } from 'react'; +import React, { useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { RouteComponentProps } from 'react-router-dom'; -import { EuiCallOut, EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { EuiCallOut, EuiPageContentBody, EuiPageHeader, EuiSpacer } from '@elastic/eui'; import { Repository, EmptyRepository } from '../../../../common/types'; -import { SectionError, Error } from '../../../shared_imports'; -import { RepositoryForm, SectionLoading } from '../../components'; +import { PageError, SectionError, Error } from '../../../shared_imports'; +import { RepositoryForm, PageLoading } from '../../components'; import { BASE_PATH, Section } from '../../constants'; import { useServices } from '../../app_context'; import { breadcrumbService, docTitleService } from '../../services/navigation'; @@ -79,12 +79,12 @@ export const RepositoryEdit: React.FunctionComponent { return ( - + - + ); }; @@ -106,7 +106,7 @@ export const RepositoryEdit: React.FunctionComponent { + setSaveError(null); + }; + const renderSaveError = () => { return saveError ? ( { - setSaveError(null); - }; - - const renderContent = () => { - if (loadingRepository) { - return renderLoading(); - } - if (repositoryError) { - return renderError(); - } - - const { isManagedRepository } = repositoryData; - - return ( - - {isManagedRepository ? ( - - - } - /> - - - ) : null} - - - ); - }; - return ( - - - -

+ + -

-
- - {renderContent()} -
-
+ + } + /> + + + + {isManagedRepository ? ( + <> + + } + /> + + + ) : null} + + + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/restore_snapshot/restore_snapshot.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/restore_snapshot/restore_snapshot.tsx index 685f3c9346f49..0f950ef3234ba 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/restore_snapshot/restore_snapshot.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/restore_snapshot/restore_snapshot.tsx @@ -8,12 +8,12 @@ import React, { useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { RouteComponentProps } from 'react-router-dom'; -import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { EuiPageContentBody, EuiPageHeader, EuiSpacer } from '@elastic/eui'; import { SnapshotDetails, RestoreSettings } from '../../../../common/types'; -import { SectionError, Error } from '../../../shared_imports'; +import { SectionError, Error, PageError } from '../../../shared_imports'; import { BASE_PATH } from '../../constants'; -import { SectionLoading, RestoreSnapshotForm } from '../../components'; +import { PageLoading, RestoreSnapshotForm } from '../../components'; import { useServices } from '../../app_context'; import { breadcrumbService, docTitleService } from '../../services/navigation'; import { useLoadSnapshot, executeRestore } from '../../services/http'; @@ -76,12 +76,12 @@ export const RestoreSnapshot: React.FunctionComponent { return ( - + - + ); }; @@ -103,8 +103,9 @@ export const RestoreSnapshot: React.FunctionComponent { - if (loadingSnapshot) { - return renderLoading(); - } - if (snapshotError) { - return renderError(); - } + if (loadingSnapshot) { + return renderLoading(); + } - return ( - - ); - }; + if (snapshotError) { + return renderError(); + } return ( - - - -

+ + -

-
- - {renderContent()} -
-
+ + } + /> + + + + + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/shared_imports.ts b/x-pack/plugins/snapshot_restore/public/shared_imports.ts index c38f0daedf996..759453edaba5d 100644 --- a/x-pack/plugins/snapshot_restore/public/shared_imports.ts +++ b/x-pack/plugins/snapshot_restore/public/shared_imports.ts @@ -12,6 +12,7 @@ export { Frequency, NotAuthorizedSection, SectionError, + PageError, sendRequest, SendRequestConfig, SendRequestResponse, diff --git a/x-pack/test/functional_with_es_ssl/apps/ml/alert_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/ml/alert_flyout.ts index 63326448ec1e5..777e6fd598f45 100644 --- a/x-pack/test/functional_with_es_ssl/apps/ml/alert_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/ml/alert_flyout.ts @@ -67,7 +67,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { let testJobId = ''; - describe('anomaly detection alert', function () { + // Failing: See https://github.com/elastic/kibana/issues/102012 + describe.skip('anomaly detection alert', function () { this.tags('ciGroup13'); before(async () => { diff --git a/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js index b678e88bcf0df..502c950d2b113 100644 --- a/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js +++ b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js @@ -15,6 +15,8 @@ const ARCHIVE = resolve(INTEGRATION_TEST_ROOT, 'test/es_archives/metricbeat'); export default function ({ getService, getPageObjects, updateBaselines }) { const screenshot = getService('screenshots'); const browser = getService('browser'); + const find = getService('find'); + const log = getService('log'); const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'dashboard', 'timePicker']); @@ -28,7 +30,7 @@ export default function ({ getService, getPageObjects, updateBaselines }) { 'dashboard', 'view/Metricbeat-system-overview-ecs?_g=(filters:!(),refreshInterval:(pause:!t,value:0),' + 'time:(from:%272020-09-29T19:02:37.902Z%27,to:%272020-09-29T19:06:43.218Z%27))&_a=' + - '(description:%27Overview%20of%20system%20metrics%27,filters:!(),fullScreenMode:!t,' + + '(description:%27Overview%20of%20system%20metrics%27,filters:!(),' + 'options:(darkTheme:!f),query:(language:kuery,query:%27%27),timeRestore:!f,' + 'title:%27%5BMetricbeat%20System%5D%20Overview%20ECS%27,viewMode:view)', { @@ -45,6 +47,7 @@ export default function ({ getService, getPageObjects, updateBaselines }) { // await PageObjects.dashboard.clickFullScreenMode(); await PageObjects.common.sleep(2000); + await find.clickByButtonText('Dismiss'); await PageObjects.dashboard.waitForRenderComplete(); await browser.setScreenshotSize(1000, 1000); }); @@ -61,7 +64,7 @@ export default function ({ getService, getPageObjects, updateBaselines }) { ); expect(percentDifference).to.be.lessThan(0.01); } finally { - await PageObjects.dashboard.clickExitFullScreenLogoButton(); + log.debug('### Screenshot taken'); } }); }); diff --git a/yarn.lock b/yarn.lock index a12920f72ba82..a9a81585000b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9293,17 +9293,16 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chromedriver@^90.0.0: - version "90.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-90.0.0.tgz#1b18960a31a12884981bdc270b43c4356ce7a65a" - integrity sha512-k+GMmNb7cmuCCctQvUIeNxDGSq8DJauO+UKQS2qLT8aA36CPEcv8rpFepf6lRkNaIlfwdCUt/0B5bZDw3wY2yw== +chromedriver@^91.0.1: + version "91.0.1" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-91.0.1.tgz#4d70a569901e356c978a41de3019c464f2a8ebd0" + integrity sha512-9LktpHiUxM4UWUsr+jI1K1YKx2GENt6BKKJ2mibPj1Wc6ODzX/3fFIlr8CZ4Ftuyga+dHTTbAyPWKwKvybEbKA== dependencies: "@testim/chrome-version" "^1.0.7" axios "^0.21.1" del "^6.0.0" extract-zip "^2.0.1" https-proxy-agent "^5.0.0" - mkdirp "^1.0.4" proxy-from-env "^1.1.0" tcp-port-used "^1.0.1"