[Enterprise Search] Replace Engines UI Settings feature flag (#149996)

## Summary

Replaced the Engines UI Settings feature flag with product access field
from the client config. Then removed the Engines UI Setting.

We opted for a backend feature flag instead of the UI setting so that a
single feature flag could be used to gate both the UI and the API for
engines for the 8.7 release, since this work will not be completed until
8.8.
This commit is contained in:
Rodney Norris 2023-02-01 11:24:08 -06:00 committed by GitHub
parent f002889cca
commit e8eb04420e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 38 additions and 43 deletions

View file

@ -570,8 +570,4 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
},
'enterpriseSearch:enableEnginesSection': {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
},
};

View file

@ -152,5 +152,4 @@ export interface UsageStats {
'securitySolution:enableGroupedNav': boolean;
'securitySolution:showRelatedIntegrations': boolean;
'visualization:visualize:legacyGaugeChartsLibrary': boolean;
'enterpriseSearch:enableEnginesSection': boolean;
}

View file

@ -9113,12 +9113,6 @@
"_meta": {
"description": "Non-default value of setting."
}
},
"enterpriseSearch:enableEnginesSection": {
"type": "boolean",
"_meta": {
"description": "Non-default value of setting."
}
}
}
},

View file

@ -29,6 +29,7 @@ export const DEFAULT_INITIAL_APP_DATA = {
},
access: {
hasAppSearchAccess: true,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: true,
},
appSearch: {

View file

@ -32,6 +32,7 @@ export interface ConfiguredLimits {
export interface ProductAccess {
hasAppSearchAccess: boolean;
hasSearchEnginesAccess: boolean;
hasWorkplaceSearchAccess: boolean;
}

View file

@ -6,4 +6,3 @@
*/
export const enterpriseSearchFeatureId = 'enterpriseSearch';
export const enableEnginesSection = 'enterpriseSearch:enableEnginesSection';

View file

@ -29,6 +29,7 @@ export const mockKibanaValues = {
navigateToUrl: jest.fn(),
productAccess: {
hasAppSearchAccess: true,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: true,
},
uiSettings: uiSettingsServiceMock.createStartContract(),

View file

@ -10,7 +10,6 @@ import { Route, Switch } from 'react-router-dom';
import { useValues } from 'kea';
import { enableEnginesSection } from '../../../../../common/ui_settings_keys';
import { KibanaLogic } from '../../../shared/kibana';
import { ENGINES_PATH, ENGINE_PATH } from '../../routes';
@ -20,8 +19,8 @@ import { NotFound } from '../not_found';
import { EnginesList } from './engines_list';
export const EnginesRouter: React.FC = () => {
const { uiSettings } = useValues(KibanaLogic);
const enginesSectionEnabled = uiSettings?.get<boolean>(enableEnginesSection, false);
const { productAccess } = useValues(KibanaLogic);
const enginesSectionEnabled = productAccess.hasSearchEnginesAccess;
if (!enginesSectionEnabled) {
return (
<Switch>

View file

@ -45,6 +45,7 @@ export const renderApp = (
const noProductAccess: ProductAccess = {
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
};
const productAccess = data.access || noProductAccess;

View file

@ -15,8 +15,6 @@ import { EuiSideNavItemType } from '@elastic/eui';
import { ProductAccess } from '../../../../common/types';
import { enableEnginesSection } from '../../../../common/ui_settings_keys';
import { useEnterpriseSearchNav, useEnterpriseSearchEngineNav } from './nav';
describe('useEnterpriseSearchContentNav', () => {
@ -28,6 +26,7 @@ describe('useEnterpriseSearchContentNav', () => {
it('returns an array of top-level Enterprise Search nav items', () => {
const fullProductAccess: ProductAccess = {
hasAppSearchAccess: true,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: true,
};
setMockValues({ productAccess: fullProductAccess });
@ -93,12 +92,12 @@ describe('useEnterpriseSearchContentNav', () => {
name: 'Search',
},
]);
expect(mockKibanaValues.uiSettings.get).toHaveBeenCalledWith(enableEnginesSection, false);
});
it('excludes legacy products when the user has no access to them', () => {
const noProductAccess: ProductAccess = {
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
};
@ -129,6 +128,7 @@ describe('useEnterpriseSearchContentNav', () => {
it('excludes App Search when the user has no access to it', () => {
const workplaceSearchProductAccess: ProductAccess = {
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: true,
};
@ -163,6 +163,7 @@ describe('useEnterpriseSearchContentNav', () => {
it('excludes Workplace Search when the user has no access to it', () => {
const appSearchProductAccess: ProductAccess = {
hasAppSearchAccess: true,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
};
@ -197,6 +198,7 @@ describe('useEnterpriseSearchContentNav', () => {
it('excludes engines when feature flag is off', () => {
const fullProductAccess: ProductAccess = {
hasAppSearchAccess: true,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: true,
};
setMockValues({ productAccess: fullProductAccess });
@ -209,12 +211,12 @@ describe('useEnterpriseSearchContentNav', () => {
describe('useEnterpriseSearchContentNav Engines feature flag', () => {
beforeEach(() => {
jest.clearAllMocks();
mockKibanaValues.uiSettings.get.mockReturnValue(true);
});
it('returns an array of top-level Enterprise Search nav items', () => {
const fullProductAccess: ProductAccess = {
hasAppSearchAccess: true,
hasSearchEnginesAccess: true,
hasWorkplaceSearchAccess: true,
};
setMockValues({ productAccess: fullProductAccess });
@ -286,12 +288,12 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => {
name: 'Standalone Experiences',
},
]);
expect(mockKibanaValues.uiSettings.get).toHaveBeenCalledWith(enableEnginesSection, false);
});
it('excludes standalone experiences when the user has no access to them', () => {
const fullProductAccess: ProductAccess = {
hasAppSearchAccess: false,
hasSearchEnginesAccess: true,
hasWorkplaceSearchAccess: false,
};
setMockValues({ productAccess: fullProductAccess });
@ -302,6 +304,7 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => {
it('excludes App Search when the user has no access to it', () => {
const fullProductAccess: ProductAccess = {
hasAppSearchAccess: false,
hasSearchEnginesAccess: true,
hasWorkplaceSearchAccess: true,
};
setMockValues({ productAccess: fullProductAccess });
@ -324,6 +327,7 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => {
it('excludes Workplace Search when the user has no access to it', () => {
const fullProductAccess: ProductAccess = {
hasAppSearchAccess: true,
hasSearchEnginesAccess: true,
hasWorkplaceSearchAccess: false,
};
setMockValues({ productAccess: fullProductAccess });
@ -351,6 +355,7 @@ describe('useEnterpriseSearchEngineNav', () => {
mockKibanaValues.uiSettings.get.mockReturnValue(true);
const fullProductAccess: ProductAccess = {
hasAppSearchAccess: true,
hasSearchEnginesAccess: true,
hasWorkplaceSearchAccess: true,
};
setMockValues({ productAccess: fullProductAccess });
@ -424,7 +429,6 @@ describe('useEnterpriseSearchEngineNav', () => {
name: 'Standalone Experiences',
},
]);
expect(mockKibanaValues.uiSettings.get).toHaveBeenCalledWith(enableEnginesSection, false);
});
it('returns selected engine sub nav items', () => {

View file

@ -19,7 +19,6 @@ import {
SEARCH_EXPERIENCES_PLUGIN,
WORKPLACE_SEARCH_PLUGIN,
} from '../../../../common/constants';
import { enableEnginesSection } from '../../../../common/ui_settings_keys';
import {
ENGINES_PATH,
SEARCH_INDICES_PATH,
@ -31,9 +30,9 @@ import { KibanaLogic } from '../kibana';
import { generateNavLink } from './nav_link_helpers';
export const useEnterpriseSearchNav = () => {
const { productAccess, uiSettings } = useValues(KibanaLogic);
const { productAccess } = useValues(KibanaLogic);
const enginesSectionEnabled = uiSettings?.get<boolean>(enableEnginesSection, false);
const enginesSectionEnabled = productAccess.hasSearchEnginesAccess;
const navItems: Array<EuiSideNavItemType<unknown>> = [
{

View file

@ -59,6 +59,7 @@ describe('checkAccess', () => {
};
expect(await checkAccess({ ...mockDependencies, security })).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
});
});
@ -71,6 +72,7 @@ describe('checkAccess', () => {
};
expect(await checkAccess({ ...mockDependencies, request })).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
});
});
@ -81,6 +83,7 @@ describe('checkAccess', () => {
mockSpaces.spacesService.getActiveSpace.mockResolvedValueOnce(disabledSpace);
expect(await checkAccess({ ...mockDependencies })).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
});
});
@ -94,6 +97,7 @@ describe('checkAccess', () => {
);
expect(await checkAccess({ ...mockDependencies })).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
});
});
@ -134,6 +138,7 @@ describe('checkAccess', () => {
};
expect(await checkAccess({ ...mockDependencies, security })).toEqual({
hasAppSearchAccess: true,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: true,
});
});
@ -149,6 +154,7 @@ describe('checkAccess', () => {
};
expect(await checkAccess({ ...mockDependencies, security })).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
});
});
@ -170,6 +176,7 @@ describe('checkAccess', () => {
const config = { host: undefined };
expect(await checkAccess({ ...mockDependencies, config })).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
});
});
@ -180,11 +187,13 @@ describe('checkAccess', () => {
(callEnterpriseSearchConfigAPI as jest.Mock).mockImplementationOnce(() => ({
access: {
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: true,
},
}));
expect(await checkAccess(mockDependencies)).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: true,
});
});
@ -193,6 +202,7 @@ describe('checkAccess', () => {
(callEnterpriseSearchConfigAPI as jest.Mock).mockImplementationOnce(() => ({}));
expect(await checkAccess(mockDependencies)).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
});
});
@ -204,6 +214,7 @@ describe('checkAccess', () => {
}));
expect(await checkAccess(mockDependencies)).toEqual({
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
});
});

View file

@ -23,12 +23,14 @@ interface CheckAccess {
log: Logger;
}
const ALLOW_ALL_PLUGINS = {
const ALLOW_ALL_PLUGINS: ProductAccess = {
hasAppSearchAccess: true,
hasSearchEnginesAccess: false, // still false unless Feature Flag explicitly enabled on backend
hasWorkplaceSearchAccess: true,
};
const DENY_ALL_PLUGINS = {
const DENY_ALL_PLUGINS: ProductAccess = {
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
};

View file

@ -70,6 +70,7 @@ describe('callEnterpriseSearchConfigAPI', () => {
name: 'someuser',
access: {
app_search: true,
search_engines: true,
workplace_search: false,
},
app_search: {
@ -123,6 +124,7 @@ describe('callEnterpriseSearchConfigAPI', () => {
kibanaVersion: '1.0.0',
access: {
hasAppSearchAccess: true,
hasSearchEnginesAccess: true,
hasWorkplaceSearchAccess: false,
},
publicUrl: 'http://some.vanity.url',
@ -136,6 +138,7 @@ describe('callEnterpriseSearchConfigAPI', () => {
kibanaVersion: '1.0.0',
access: {
hasAppSearchAccess: false,
hasSearchEnginesAccess: false,
hasWorkplaceSearchAccess: false,
},
publicUrl: undefined,

View file

@ -87,6 +87,7 @@ export const callEnterpriseSearchConfigAPI = async ({
kibanaVersion: kibanaPackageJson.version,
access: {
hasAppSearchAccess: !!data?.current_user?.access?.app_search,
hasSearchEnginesAccess: !!data?.current_user?.access?.search_engines,
hasWorkplaceSearchAccess: !!data?.current_user?.access?.workplace_search,
},
publicUrl: stripTrailingSlash(data?.settings?.external_url),

View file

@ -5,25 +5,9 @@
* 2.0.
*/
import { schema } from '@kbn/config-schema';
import { UiSettingsParams } from '@kbn/core/types';
import { i18n } from '@kbn/i18n';
import { enterpriseSearchFeatureId, enableEnginesSection } from '../common/ui_settings_keys';
/**
* uiSettings definitions for Enterprise Search
*/
export const uiSettings: Record<string, UiSettingsParams<boolean>> = {
[enableEnginesSection]: {
category: [enterpriseSearchFeatureId],
description: i18n.translate('xpack.enterpriseSearch.uiSettings.engines.description', {
defaultMessage: 'Enable the new Engines section in Enterprise Search.',
}),
name: i18n.translate('xpack.enterpriseSearch.uiSettings.engines.name', {
defaultMessage: 'Enable Engines',
}),
requiresPageReload: false,
schema: schema.boolean(),
value: false,
},
};
export const uiSettings: Record<string, UiSettingsParams<boolean>> = {};