Specifying valid licenses for the Graph feature (#55911)

* Specifying valid licenses for the Graph feature

* Adding option to /api/features to ignore valid licenses

This allow us to take advantage of the /api/featues endpoint within our
tests to disable all features, including those which are disabled by the
current license. The ui capabilities don't take into considerating the
license at the moment, so they're separate entirely separeate mechanisms
at this point in time.

* Addressing PR comments

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Brandon Kobel 2020-02-07 10:27:55 -08:00 committed by GitHub
parent b2c9beb00f
commit 02f309c206
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 80 additions and 9 deletions

View file

@ -61,6 +61,7 @@ export const graph: LegacyPluginInitializer = kibana => {
navLinkId: 'graph',
app: ['graph', 'kibana'],
catalogue: ['graph'],
validLicenses: ['platinum', 'enterprise', 'trial'],
privileges: {
all: {
savedObject: {

View file

@ -43,7 +43,7 @@ export interface Feature<
* This does not restrict access to your feature based on license.
* Its only purpose is to inform the space and roles UIs on which features to display.
*/
validLicenses?: Array<'basic' | 'standard' | 'gold' | 'platinum' | 'enterprise'>;
validLicenses?: Array<'basic' | 'standard' | 'gold' | 'platinum' | 'enterprise' | 'trial'>;
/**
* An optional EUI Icon to be used when displaying your feature.

View file

@ -51,7 +51,7 @@ const schema = Joi.object({
name: Joi.string().required(),
excludeFromBasePrivileges: Joi.boolean(),
validLicenses: Joi.array().items(
Joi.string().valid('basic', 'standard', 'gold', 'platinum', 'enterprise')
Joi.string().valid('basic', 'standard', 'gold', 'platinum', 'enterprise', 'trial')
),
icon: Joi.string(),
description: Joi.string(),

View file

@ -53,7 +53,7 @@ describe('GET /api/features', () => {
it('returns a list of available features', async () => {
const mockResponse = httpServerMock.createResponseFactory();
routeHandler(undefined as any, undefined as any, mockResponse);
routeHandler(undefined as any, { query: {} } as any, mockResponse);
expect(mockResponse.ok.mock.calls).toMatchInlineSnapshot(`
Array [
@ -84,11 +84,11 @@ describe('GET /api/features', () => {
`);
});
it(`does not return features that arent allowed by current license`, async () => {
it(`by default does not return features that arent allowed by current license`, async () => {
currentLicenseLevel = 'basic';
const mockResponse = httpServerMock.createResponseFactory();
routeHandler(undefined as any, undefined as any, mockResponse);
routeHandler(undefined as any, { query: {} } as any, mockResponse);
expect(mockResponse.ok.mock.calls).toMatchInlineSnapshot(`
Array [
@ -107,4 +107,63 @@ describe('GET /api/features', () => {
]
`);
});
it(`ignoreValidLicenses=false does not return features that arent allowed by current license`, async () => {
currentLicenseLevel = 'basic';
const mockResponse = httpServerMock.createResponseFactory();
routeHandler(undefined as any, { query: { ignoreValidLicenses: false } } as any, mockResponse);
expect(mockResponse.ok.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
Object {
"body": Array [
Object {
"app": Array [],
"id": "feature_1",
"name": "Feature 1",
"privileges": Object {},
},
],
},
],
]
`);
});
it(`ignoreValidLicenses=true returns features that arent allowed by current license`, async () => {
currentLicenseLevel = 'basic';
const mockResponse = httpServerMock.createResponseFactory();
routeHandler(undefined as any, { query: { ignoreValidLicenses: true } } as any, mockResponse);
expect(mockResponse.ok.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
Object {
"body": Array [
Object {
"app": Array [],
"id": "feature_1",
"name": "Feature 1",
"privileges": Object {},
},
Object {
"app": Array [
"bar-app",
],
"id": "licensed_feature",
"name": "Licensed Feature",
"privileges": Object {},
"validLicenses": Array [
"gold",
],
},
],
},
],
]
`);
});
});

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../../../../src/core/server';
import { LegacyAPI } from '../plugin';
import { FeatureRegistry } from '../feature_registry';
@ -19,13 +20,20 @@ export interface RouteDefinitionParams {
export function defineRoutes({ router, featureRegistry, getLegacyAPI }: RouteDefinitionParams) {
router.get(
{ path: '/api/features', options: { tags: ['access:features'] }, validate: false },
{
path: '/api/features',
options: { tags: ['access:features'] },
validate: {
query: schema.object({ ignoreValidLicenses: schema.boolean({ defaultValue: false }) }),
},
},
(context, request, response) => {
const allFeatures = featureRegistry.getAll();
return response.ok({
body: allFeatures.filter(
feature =>
request.query.ignoreValidLicenses ||
!feature.validLicenses ||
!feature.validLicenses.length ||
getLegacyAPI().xpackInfo.license.isOneOf(feature.validLicenses)

View file

@ -22,9 +22,11 @@ export class FeaturesService {
});
}
public async get(): Promise<Features> {
public async get({ ignoreValidLicenses } = { ignoreValidLicenses: false }): Promise<Features> {
this.log.debug('requesting /api/features to get the features');
const response = await this.axios.get('/api/features');
const response = await this.axios.get(
`/api/features?ignoreValidLicenses=${ignoreValidLicenses}`
);
if (response.status !== 200) {
throw new Error(

View file

@ -16,7 +16,8 @@ export default function uiCapabilitesTests({ loadTestFile, getService }: FtrProv
this.tags('ciGroup9');
before(async () => {
const features = await featuresService.get();
// we're using a basic license, so if we want to disable all features, we have to ignore the valid licenses
const features = await featuresService.get({ ignoreValidLicenses: true });
for (const space of SpaceScenarios) {
const disabledFeatures =
space.disabledFeatures === '*' ? Object.keys(features) : space.disabledFeatures;