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:
Larry Gregory 2020-07-09 16:50:00 -04:00 committed by GitHub
parent c7f3d9219f
commit a32b9e89b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 12 deletions

View file

@ -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();

View file

@ -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;
}
}
});
});