mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Support multiple features declaring same properties (#71106)
Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
c7f3d9219f
commit
a32b9e89b6
2 changed files with 83 additions and 12 deletions
|
@ -59,6 +59,27 @@ const features = ([
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// feature 4 intentionally delcares the same items as feature 3
|
||||
id: 'feature_4',
|
||||
name: 'Feature 4',
|
||||
navLinkId: 'feature3',
|
||||
app: ['feature3', 'feature3_app'],
|
||||
catalogue: ['feature3Entry'],
|
||||
management: {
|
||||
kibana: ['indices'],
|
||||
},
|
||||
privileges: {
|
||||
all: {
|
||||
app: [],
|
||||
ui: [],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
] as unknown) as Feature[];
|
||||
|
||||
const buildCapabilities = () =>
|
||||
|
@ -73,6 +94,7 @@ const buildCapabilities = () =>
|
|||
catalogue: {
|
||||
discover: true,
|
||||
visualize: false,
|
||||
feature3Entry: true,
|
||||
},
|
||||
management: {
|
||||
kibana: {
|
||||
|
@ -217,11 +239,38 @@ describe('capabilitiesSwitcher', () => {
|
|||
expect(result).toEqual(expectedCapabilities);
|
||||
});
|
||||
|
||||
it('does not disable catalogue, management, or app entries when they are shared with an enabled feature', async () => {
|
||||
const space: Space = {
|
||||
id: 'space',
|
||||
name: '',
|
||||
disabledFeatures: ['feature_3'],
|
||||
};
|
||||
|
||||
const capabilities = buildCapabilities();
|
||||
|
||||
const { switcher } = setup(space);
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
const result = await switcher(request, capabilities);
|
||||
|
||||
const expectedCapabilities = buildCapabilities();
|
||||
|
||||
// These capabilities are shared by feature_4, which is enabled
|
||||
expectedCapabilities.navLinks.feature3 = true;
|
||||
expectedCapabilities.navLinks.feature3_app = true;
|
||||
expectedCapabilities.catalogue.feature3Entry = true;
|
||||
expectedCapabilities.management.kibana.indices = true;
|
||||
// These capabilities are only exposed by feature_3, which is disabled
|
||||
expectedCapabilities.feature_3.bar = false;
|
||||
expectedCapabilities.feature_3.foo = false;
|
||||
|
||||
expect(result).toEqual(expectedCapabilities);
|
||||
});
|
||||
|
||||
it('can disable everything', async () => {
|
||||
const space: Space = {
|
||||
id: 'space',
|
||||
name: '',
|
||||
disabledFeatures: ['feature_1', 'feature_2', 'feature_3'],
|
||||
disabledFeatures: ['feature_1', 'feature_2', 'feature_3', 'feature_4'],
|
||||
};
|
||||
|
||||
const capabilities = buildCapabilities();
|
||||
|
|
|
@ -54,22 +54,38 @@ function toggleDisabledFeatures(
|
|||
) {
|
||||
const disabledFeatureKeys = activeSpace.disabledFeatures;
|
||||
|
||||
const disabledFeatures = disabledFeatureKeys
|
||||
.map((key) => features.find((feature) => feature.id === key))
|
||||
.filter((feature) => typeof feature !== 'undefined') as Feature[];
|
||||
const [enabledFeatures, disabledFeatures] = features.reduce(
|
||||
(acc, feature) => {
|
||||
if (disabledFeatureKeys.includes(feature.id)) {
|
||||
return [acc[0], [...acc[1], feature]];
|
||||
}
|
||||
return [[...acc[0], feature], acc[1]];
|
||||
},
|
||||
[[], []] as [Feature[], Feature[]]
|
||||
);
|
||||
|
||||
const navLinks = capabilities.navLinks;
|
||||
const catalogueEntries = capabilities.catalogue;
|
||||
const managementItems = capabilities.management;
|
||||
|
||||
const enabledAppEntries = new Set(enabledFeatures.flatMap((ef) => ef.app ?? []));
|
||||
const enabledCatalogueEntries = new Set(enabledFeatures.flatMap((ef) => ef.catalogue ?? []));
|
||||
const enabledManagementEntries = enabledFeatures.reduce((acc, feature) => {
|
||||
const sections = Object.entries(feature.management ?? {});
|
||||
sections.forEach((section) => {
|
||||
if (!acc.has(section[0])) {
|
||||
acc.set(section[0], []);
|
||||
}
|
||||
acc.get(section[0])!.push(...section[1]);
|
||||
});
|
||||
return acc;
|
||||
}, new Map<string, string[]>());
|
||||
|
||||
for (const feature of disabledFeatures) {
|
||||
// Disable associated navLink, if one exists
|
||||
if (feature.navLinkId && navLinks.hasOwnProperty(feature.navLinkId)) {
|
||||
navLinks[feature.navLinkId] = false;
|
||||
}
|
||||
|
||||
feature.app.forEach((app) => {
|
||||
if (navLinks.hasOwnProperty(app)) {
|
||||
const featureNavLinks = feature.navLinkId ? [feature.navLinkId, ...feature.app] : feature.app;
|
||||
featureNavLinks.forEach((app) => {
|
||||
if (navLinks.hasOwnProperty(app) && !enabledAppEntries.has(app)) {
|
||||
navLinks[app] = false;
|
||||
}
|
||||
});
|
||||
|
@ -77,18 +93,24 @@ function toggleDisabledFeatures(
|
|||
// Disable associated catalogue entries
|
||||
const privilegeCatalogueEntries = feature.catalogue || [];
|
||||
privilegeCatalogueEntries.forEach((catalogueEntryId) => {
|
||||
catalogueEntries[catalogueEntryId] = false;
|
||||
if (!enabledCatalogueEntries.has(catalogueEntryId)) {
|
||||
catalogueEntries[catalogueEntryId] = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Disable associated management items
|
||||
const privilegeManagementSections = feature.management || {};
|
||||
Object.entries(privilegeManagementSections).forEach(([sectionId, sectionItems]) => {
|
||||
sectionItems.forEach((item) => {
|
||||
const enabledManagementEntriesSection = enabledManagementEntries.get(sectionId);
|
||||
if (
|
||||
managementItems.hasOwnProperty(sectionId) &&
|
||||
managementItems[sectionId].hasOwnProperty(item)
|
||||
) {
|
||||
managementItems[sectionId][item] = false;
|
||||
const isEnabledElsewhere = (enabledManagementEntriesSection ?? []).includes(item);
|
||||
if (!isEnabledElsewhere) {
|
||||
managementItems[sectionId][item] = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue