Skip to content

Commit

Permalink
Merge branch 'master' into loader
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Oct 7, 2020
2 parents 1e81d25 + a8c080b commit b5e7995
Show file tree
Hide file tree
Showing 21 changed files with 400 additions and 237 deletions.
4 changes: 2 additions & 2 deletions src/core/public/overlays/banners/banners_list.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('BannersList', () => {
]);

expect(mount(<BannersList banners$={banners$} />).html()).toMatchInlineSnapshot(
`"<div class=\\"kbnGlobalBannerList\\"><div data-test-priority=\\"0\\" class=\\"kbnGlobalBannerList__item\\"><h1>Hello!</h1></div></div>"`
`"<div class=\\"kbnGlobalBannerList\\"><div data-test-priority=\\"0\\" class=\\"kbnGlobalBannerList__item\\" data-test-subj=\\"global-banner-item\\"><h1>Hello!</h1></div></div>"`
);
});

Expand Down Expand Up @@ -85,7 +85,7 @@ describe('BannersList', () => {

// Two new banners should be rendered
expect(component.html()).toMatchInlineSnapshot(
`"<div class=\\"kbnGlobalBannerList\\"><div data-test-priority=\\"1\\" class=\\"kbnGlobalBannerList__item\\"><h1>First Banner!</h1></div><div data-test-priority=\\"0\\" class=\\"kbnGlobalBannerList__item\\"><h1>Second banner!</h1></div></div>"`
`"<div class=\\"kbnGlobalBannerList\\"><div data-test-priority=\\"1\\" class=\\"kbnGlobalBannerList__item\\" data-test-subj=\\"global-banner-item\\"><h1>First Banner!</h1></div><div data-test-priority=\\"0\\" class=\\"kbnGlobalBannerList__item\\" data-test-subj=\\"global-banner-item\\"><h1>Second banner!</h1></div></div>"`
);
// Original banner should be unmounted
expect(unmount).toHaveBeenCalled();
Expand Down
7 changes: 6 additions & 1 deletion src/core/public/overlays/banners/banners_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ const BannerItem: React.FunctionComponent<{ banner: OverlayBanner }> = ({ banner
useEffect(() => banner.mount(element.current!), [banner]); // Only unmount / remount if banner object changed.

return (
<div data-test-priority={banner.priority} className="kbnGlobalBannerList__item" ref={element} />
<div
data-test-priority={banner.priority}
className="kbnGlobalBannerList__item"
ref={element}
data-test-subj="global-banner-item"
/>
);
};
10 changes: 10 additions & 0 deletions test/functional/page_objects/common_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,16 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo
async scrollKibanaBodyTop() {
await browser.setScrollToById('kibana-body', 0, 0);
}

/**
* Dismiss Banner if available.
*/
async dismissBanner() {
if (await testSubjects.exists('global-banner-item')) {
const button = await find.byButtonText('Dismiss');
await button.click();
}
}
}

return new CommonPage();
Expand Down
1 change: 1 addition & 0 deletions test/scripts/jenkins_xpack_build_plugins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ node scripts/build_kibana_platform_plugins \
--scan-dir "$XPACK_DIR/test/alerting_api_integration/plugins" \
--scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \
--scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \
--scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \
--workers 12 \
--verbose
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { i18n } from '@kbn/i18n';
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
import { APIReturnType } from '../../../../services/rest/createCallApmApi';
import { APMLink } from './APMLink';
import { getEnvironmentLabel } from '../../../../../common/environment_filter_values';
import {
ENVIRONMENT_ALL,
getEnvironmentLabel,
} from '../../../../../common/environment_filter_values';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { useFetcher, FETCH_STATUS } from '../../../../hooks/useFetcher';
import { useLicense } from '../../../../hooks/useLicense';
Expand Down Expand Up @@ -57,7 +60,8 @@ export function MissingJobsAlert({ environment }: { environment?: string }) {
return null;
}

const isEnvironmentSelected = !!environment;
const isEnvironmentSelected =
environment && environment !== ENVIRONMENT_ALL.value;

// there are jobs for at least one environment
if (!isEnvironmentSelected && data.jobs.length > 0) {
Expand All @@ -80,7 +84,7 @@ export function MissingJobsAlert({ environment }: { environment?: string }) {
}

function getTooltipText(environment?: string) {
if (!environment) {
if (!environment || environment === ENVIRONMENT_ALL.value) {
return i18n.translate('xpack.apm.anomalyDetectionSetup.notEnabledText', {
defaultMessage: `Anomaly detection is not yet enabled. Click to continue setup.`,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,15 @@ describe('MissingJobsAlert', () => {
expect(toolTipText).toBe(undefined);
});
});

describe('when at least one job exists and all environments are selected', () => {
it('does not show a warning', async () => {
const { toolTipAnchor, toolTipText } = await renderTooltipAnchor({
jobs: [{ environment: 'ENVIRONMENT_ALL', job_id: 'my_job_id' }],
});

expect(toolTipAnchor).not.toBeInTheDocument();
expect(toolTipText).toBe(undefined);
});
});
});
2 changes: 1 addition & 1 deletion x-pack/plugins/licensing/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
return {
refresh: refreshManually,
license$,
featureUsage: this.featureUsage.setup({ http: core.http }),
featureUsage: this.featureUsage.setup(),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,33 @@ describe('FeatureUsageService', () => {

describe('#setup', () => {
describe('#register', () => {
it('calls the endpoint with the correct parameters', async () => {
const setup = service.setup({ http });
await setup.register('my-feature', 'platinum');
it('calls the endpoint on start with the correct parameters', async () => {
const setup = service.setup();
setup.register('my-feature', 'platinum');
setup.register('my-other-feature', 'gold');
expect(http.post).not.toHaveBeenCalled();

service.start({ http });
expect(http.post).toHaveBeenCalledTimes(1);
expect(http.post).toHaveBeenCalledWith('/internal/licensing/feature_usage/register', {
body: JSON.stringify({
featureName: 'my-feature',
licenseType: 'platinum',
}),
body: JSON.stringify([
{ featureName: 'my-feature', licenseType: 'platinum' },
{ featureName: 'my-other-feature', licenseType: 'gold' },
]),
});
});

it('does not call endpoint if on anonymous path', async () => {
it('does not call endpoint on start if no registrations', async () => {
service.setup();
service.start({ http });
expect(http.post).not.toHaveBeenCalled();
});

it('does not call endpoint on start if on anonymous path', async () => {
http.anonymousPaths.isAnonymous.mockReturnValue(true);
const setup = service.setup({ http });
await setup.register('my-feature', 'platinum');
const setup = service.setup();
setup.register('my-feature', 'platinum');
service.start({ http });
expect(http.post).not.toHaveBeenCalled();
});
});
Expand All @@ -42,7 +53,7 @@ describe('FeatureUsageService', () => {
describe('#start', () => {
describe('#notifyUsage', () => {
it('calls the endpoint with the correct parameters', async () => {
service.setup({ http });
service.setup();
const start = service.start({ http });
await start.notifyUsage('my-feature', 42);

Expand All @@ -56,7 +67,7 @@ describe('FeatureUsageService', () => {
});

it('correctly convert dates', async () => {
service.setup({ http });
service.setup();
const start = service.start({ http });

const now = new Date();
Expand All @@ -74,7 +85,7 @@ describe('FeatureUsageService', () => {

it('does not call endpoint if on anonymous path', async () => {
http.anonymousPaths.isAnonymous.mockReturnValue(true);
service.setup({ http });
service.setup();
const start = service.start({ http });
await start.notifyUsage('my-feature', 42);
expect(http.post).not.toHaveBeenCalled();
Expand Down
36 changes: 18 additions & 18 deletions x-pack/plugins/licensing/public/services/feature_usage_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
*/

import { isDate } from 'lodash';
import type { HttpSetup, HttpStart } from 'src/core/public';
import type { HttpStart } from 'src/core/public';
import { LicenseType } from '../../common/types';

/** @public */
export interface FeatureUsageServiceSetup {
/**
* Register a feature to be able to notify of it's usages using the {@link FeatureUsageServiceStart | service start contract}.
*/
register(featureName: string, licenseType: LicenseType): Promise<void>;
register(featureName: string, licenseType: LicenseType): void;
}

/** @public */
Expand All @@ -27,10 +27,6 @@ export interface FeatureUsageServiceStart {
notifyUsage(featureName: string, usedAt?: Date | number): Promise<void>;
}

interface SetupDeps {
http: HttpSetup;
}

interface StartDeps {
http: HttpStart;
}
Expand All @@ -39,29 +35,33 @@ interface StartDeps {
* @internal
*/
export class FeatureUsageService {
public setup({ http }: SetupDeps): FeatureUsageServiceSetup {
private readonly registrations: Array<{ featureName: string; licenseType: LicenseType }> = [];

public setup(): FeatureUsageServiceSetup {
return {
register: async (featureName, licenseType) => {
// Skip registration if on logged-out page
// NOTE: this only works because the login page does a full-page refresh after logging in
// If this is ever changed, this code will need to buffer registrations and call them after the user logs in.
if (http.anonymousPaths.isAnonymous(window.location.pathname)) return;

await http.post('/internal/licensing/feature_usage/register', {
body: JSON.stringify({
featureName,
licenseType,
}),
});
this.registrations.push({ featureName, licenseType });
},
};
}

public start({ http }: StartDeps): FeatureUsageServiceStart {
// Skip registration if on logged-out page
// NOTE: this only works because the login page does a full-page refresh after logging in
// If this is ever changed, this code will need to buffer registrations and call them after the user logs in.
const registrationPromise =
http.anonymousPaths.isAnonymous(window.location.pathname) || this.registrations.length === 0
? Promise.resolve()
: http.post('/internal/licensing/feature_usage/register', {
body: JSON.stringify(this.registrations),
});

return {
notifyUsage: async (featureName, usedAt = Date.now()) => {
// Skip notification if on logged-out page
if (http.anonymousPaths.isAnonymous(window.location.pathname)) return;
// Wait for registrations to complete
await registrationPromise;

const lastUsed = isDate(usedAt) ? usedAt.getTime() : usedAt;
await http.post('/internal/licensing/feature_usage/notify', {
Expand Down
28 changes: 16 additions & 12 deletions x-pack/plugins/licensing/server/routes/internal/register_feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,26 @@ export function registerRegisterFeatureRoute(
{
path: '/internal/licensing/feature_usage/register',
validate: {
body: schema.object({
featureName: schema.string(),
licenseType: schema.string({
validate: (value) => {
if (!(value in LICENSE_TYPE)) {
return `Invalid license type: ${value}`;
}
},
}),
}),
body: schema.arrayOf(
schema.object({
featureName: schema.string(),
licenseType: schema.string({
validate: (value) => {
if (!(value in LICENSE_TYPE)) {
return `Invalid license type: ${value}`;
}
},
}),
})
),
},
},
async (context, request, response) => {
const { featureName, licenseType } = request.body;
const registrations = request.body;

featureUsageSetup.register(featureName, licenseType as LicenseType);
registrations.forEach(({ featureName, licenseType }) => {
featureUsageSetup.register(featureName, licenseType as LicenseType);
});

return response.ok({
body: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
} from '@elastic/eui';
import { ConfusionMatrix } from '../../../../common/analytics';

const COL_INITIAL_WIDTH = 165; // in pixels

interface ColumnData {
actual_class: string;
actual_class_doc_count: number;
Expand All @@ -27,10 +29,11 @@ export const MAX_COLUMNS = 6;

export function getColumnData(confusionMatrixData: ConfusionMatrix[]) {
const colData: Partial<ColumnData[]> = [];
const columns: Array<{ id: string; display?: any }> = [
const columns: Array<{ id: string; display?: any; initialWidth?: number }> = [
{
id: ACTUAL_CLASS_ID,
display: <span />,
initialWidth: COL_INITIAL_WIDTH,
},
];

Expand All @@ -51,7 +54,7 @@ export function getColumnData(confusionMatrixData: ConfusionMatrix[]) {

const predictedClasses = classData.predicted_classes || [];

columns.push({ id: classData.actual_class });
columns.push({ id: classData.actual_class, initialWidth: COL_INITIAL_WIDTH });

for (let i = 0; i < predictedClasses.length; i++) {
const predictedClass = predictedClasses[i].predicted_class;
Expand All @@ -63,7 +66,7 @@ export function getColumnData(confusionMatrixData: ConfusionMatrix[]) {
});

if (showOther) {
columns.push({ id: OTHER_CLASS_ID });
columns.push({ id: OTHER_CLASS_ID, initialWidth: COL_INITIAL_WIDTH });
}

return { columns, columnData: colData };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,7 @@ export class UiActionsServiceEnhancements

private registerFeatureUsage = (definition: ActionFactoryDefinition<any, any, any>): void => {
if (!definition.minimalLicense || !definition.licenseFeatureName) return;

// Intentionally don't wait for response because
// happens in setup phase and has to be sync
this.deps.featureUsageSetup
.register(definition.licenseFeatureName, definition.minimalLicense)
.catch(() => {
// eslint-disable-next-line no-console
console.warn(
`ActionFactory [actionFactory.id = ${definition.id}] fail to register feature for featureUsage.`
);
});
this.deps.featureUsageSetup.register(definition.licenseFeatureName, definition.minimalLicense);
};

public readonly telemetry = (state: DynamicActionsState, telemetry: Record<string, any> = {}) => {
Expand Down
Binary file not shown.
1 change: 1 addition & 0 deletions x-pack/test/licensing_plugin/config.public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
KIBANA_ROOT,
'test/plugin_functional/plugins/core_provider_plugin'
)}`,
`--plugin-path=${path.resolve(__dirname, 'plugins/test_feature_usage')}`,
],
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"id": "testFeatureUsage",
"version": "kibana",
"server": false,
"ui": true,
"requiredPlugins": ["licensing"]
}
Original file line number Diff line number Diff line change
@@ -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;
* you may not use this file except in compliance with the Elastic License.
*/

import { TestFeatureUsagePlugin } from './plugin';

export const plugin = () => new TestFeatureUsagePlugin();
Loading

0 comments on commit b5e7995

Please sign in to comment.