Skip to content

Commit b5e7995

Browse files
Merge branch 'master' into loader
2 parents 1e81d25 + a8c080b commit b5e7995

File tree

21 files changed

+400
-237
lines changed

21 files changed

+400
-237
lines changed

src/core/public/overlays/banners/banners_list.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe('BannersList', () => {
4343
]);
4444

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

@@ -85,7 +85,7 @@ describe('BannersList', () => {
8585

8686
// Two new banners should be rendered
8787
expect(component.html()).toMatchInlineSnapshot(
88-
`"<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>"`
88+
`"<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>"`
8989
);
9090
// Original banner should be unmounted
9191
expect(unmount).toHaveBeenCalled();

src/core/public/overlays/banners/banners_list.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ const BannerItem: React.FunctionComponent<{ banner: OverlayBanner }> = ({ banner
5959
useEffect(() => banner.mount(element.current!), [banner]); // Only unmount / remount if banner object changed.
6060

6161
return (
62-
<div data-test-priority={banner.priority} className="kbnGlobalBannerList__item" ref={element} />
62+
<div
63+
data-test-priority={banner.priority}
64+
className="kbnGlobalBannerList__item"
65+
ref={element}
66+
data-test-subj="global-banner-item"
67+
/>
6368
);
6469
};

test/functional/page_objects/common_page.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,16 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo
505505
async scrollKibanaBodyTop() {
506506
await browser.setScrollToById('kibana-body', 0, 0);
507507
}
508+
509+
/**
510+
* Dismiss Banner if available.
511+
*/
512+
async dismissBanner() {
513+
if (await testSubjects.exists('global-banner-item')) {
514+
const button = await find.byButtonText('Dismiss');
515+
await button.click();
516+
}
517+
}
508518
}
509519

510520
return new CommonPage();

test/scripts/jenkins_xpack_build_plugins.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ node scripts/build_kibana_platform_plugins \
1010
--scan-dir "$XPACK_DIR/test/alerting_api_integration/plugins" \
1111
--scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \
1212
--scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \
13+
--scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \
1314
--workers 12 \
1415
--verbose

x-pack/plugins/apm/public/components/shared/Links/apm/AnomalyDetectionSetupLink.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import { i18n } from '@kbn/i18n';
99
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
1010
import { APIReturnType } from '../../../../services/rest/createCallApmApi';
1111
import { APMLink } from './APMLink';
12-
import { getEnvironmentLabel } from '../../../../../common/environment_filter_values';
12+
import {
13+
ENVIRONMENT_ALL,
14+
getEnvironmentLabel,
15+
} from '../../../../../common/environment_filter_values';
1316
import { useUrlParams } from '../../../../hooks/useUrlParams';
1417
import { useFetcher, FETCH_STATUS } from '../../../../hooks/useFetcher';
1518
import { useLicense } from '../../../../hooks/useLicense';
@@ -57,7 +60,8 @@ export function MissingJobsAlert({ environment }: { environment?: string }) {
5760
return null;
5861
}
5962

60-
const isEnvironmentSelected = !!environment;
63+
const isEnvironmentSelected =
64+
environment && environment !== ENVIRONMENT_ALL.value;
6165

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

8286
function getTooltipText(environment?: string) {
83-
if (!environment) {
87+
if (!environment || environment === ENVIRONMENT_ALL.value) {
8488
return i18n.translate('xpack.apm.anomalyDetectionSetup.notEnabledText', {
8589
defaultMessage: `Anomaly detection is not yet enabled. Click to continue setup.`,
8690
});

x-pack/plugins/apm/public/components/shared/Links/apm/anomaly_detection_setup_link.test.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,15 @@ describe('MissingJobsAlert', () => {
9696
expect(toolTipText).toBe(undefined);
9797
});
9898
});
99+
100+
describe('when at least one job exists and all environments are selected', () => {
101+
it('does not show a warning', async () => {
102+
const { toolTipAnchor, toolTipText } = await renderTooltipAnchor({
103+
jobs: [{ environment: 'ENVIRONMENT_ALL', job_id: 'my_job_id' }],
104+
});
105+
106+
expect(toolTipAnchor).not.toBeInTheDocument();
107+
expect(toolTipText).toBe(undefined);
108+
});
109+
});
99110
});

x-pack/plugins/licensing/public/plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
117117
return {
118118
refresh: refreshManually,
119119
license$,
120-
featureUsage: this.featureUsage.setup({ http: core.http }),
120+
featureUsage: this.featureUsage.setup(),
121121
};
122122
}
123123

x-pack/plugins/licensing/public/services/feature_usage_service.test.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,33 @@ describe('FeatureUsageService', () => {
1818

1919
describe('#setup', () => {
2020
describe('#register', () => {
21-
it('calls the endpoint with the correct parameters', async () => {
22-
const setup = service.setup({ http });
23-
await setup.register('my-feature', 'platinum');
21+
it('calls the endpoint on start with the correct parameters', async () => {
22+
const setup = service.setup();
23+
setup.register('my-feature', 'platinum');
24+
setup.register('my-other-feature', 'gold');
25+
expect(http.post).not.toHaveBeenCalled();
26+
27+
service.start({ http });
2428
expect(http.post).toHaveBeenCalledTimes(1);
2529
expect(http.post).toHaveBeenCalledWith('/internal/licensing/feature_usage/register', {
26-
body: JSON.stringify({
27-
featureName: 'my-feature',
28-
licenseType: 'platinum',
29-
}),
30+
body: JSON.stringify([
31+
{ featureName: 'my-feature', licenseType: 'platinum' },
32+
{ featureName: 'my-other-feature', licenseType: 'gold' },
33+
]),
3034
});
3135
});
3236

33-
it('does not call endpoint if on anonymous path', async () => {
37+
it('does not call endpoint on start if no registrations', async () => {
38+
service.setup();
39+
service.start({ http });
40+
expect(http.post).not.toHaveBeenCalled();
41+
});
42+
43+
it('does not call endpoint on start if on anonymous path', async () => {
3444
http.anonymousPaths.isAnonymous.mockReturnValue(true);
35-
const setup = service.setup({ http });
36-
await setup.register('my-feature', 'platinum');
45+
const setup = service.setup();
46+
setup.register('my-feature', 'platinum');
47+
service.start({ http });
3748
expect(http.post).not.toHaveBeenCalled();
3849
});
3950
});
@@ -42,7 +53,7 @@ describe('FeatureUsageService', () => {
4253
describe('#start', () => {
4354
describe('#notifyUsage', () => {
4455
it('calls the endpoint with the correct parameters', async () => {
45-
service.setup({ http });
56+
service.setup();
4657
const start = service.start({ http });
4758
await start.notifyUsage('my-feature', 42);
4859

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

5869
it('correctly convert dates', async () => {
59-
service.setup({ http });
70+
service.setup();
6071
const start = service.start({ http });
6172

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

7586
it('does not call endpoint if on anonymous path', async () => {
7687
http.anonymousPaths.isAnonymous.mockReturnValue(true);
77-
service.setup({ http });
88+
service.setup();
7889
const start = service.start({ http });
7990
await start.notifyUsage('my-feature', 42);
8091
expect(http.post).not.toHaveBeenCalled();

x-pack/plugins/licensing/public/services/feature_usage_service.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
*/
66

77
import { isDate } from 'lodash';
8-
import type { HttpSetup, HttpStart } from 'src/core/public';
8+
import type { HttpStart } from 'src/core/public';
99
import { LicenseType } from '../../common/types';
1010

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

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

30-
interface SetupDeps {
31-
http: HttpSetup;
32-
}
33-
3430
interface StartDeps {
3531
http: HttpStart;
3632
}
@@ -39,29 +35,33 @@ interface StartDeps {
3935
* @internal
4036
*/
4137
export class FeatureUsageService {
42-
public setup({ http }: SetupDeps): FeatureUsageServiceSetup {
38+
private readonly registrations: Array<{ featureName: string; licenseType: LicenseType }> = [];
39+
40+
public setup(): FeatureUsageServiceSetup {
4341
return {
4442
register: async (featureName, licenseType) => {
45-
// Skip registration if on logged-out page
46-
// NOTE: this only works because the login page does a full-page refresh after logging in
47-
// If this is ever changed, this code will need to buffer registrations and call them after the user logs in.
48-
if (http.anonymousPaths.isAnonymous(window.location.pathname)) return;
49-
50-
await http.post('/internal/licensing/feature_usage/register', {
51-
body: JSON.stringify({
52-
featureName,
53-
licenseType,
54-
}),
55-
});
43+
this.registrations.push({ featureName, licenseType });
5644
},
5745
};
5846
}
5947

6048
public start({ http }: StartDeps): FeatureUsageServiceStart {
49+
// Skip registration if on logged-out page
50+
// NOTE: this only works because the login page does a full-page refresh after logging in
51+
// If this is ever changed, this code will need to buffer registrations and call them after the user logs in.
52+
const registrationPromise =
53+
http.anonymousPaths.isAnonymous(window.location.pathname) || this.registrations.length === 0
54+
? Promise.resolve()
55+
: http.post('/internal/licensing/feature_usage/register', {
56+
body: JSON.stringify(this.registrations),
57+
});
58+
6159
return {
6260
notifyUsage: async (featureName, usedAt = Date.now()) => {
6361
// Skip notification if on logged-out page
6462
if (http.anonymousPaths.isAnonymous(window.location.pathname)) return;
63+
// Wait for registrations to complete
64+
await registrationPromise;
6565

6666
const lastUsed = isDate(usedAt) ? usedAt.getTime() : usedAt;
6767
await http.post('/internal/licensing/feature_usage/notify', {

x-pack/plugins/licensing/server/routes/internal/register_feature.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,26 @@ export function registerRegisterFeatureRoute(
1616
{
1717
path: '/internal/licensing/feature_usage/register',
1818
validate: {
19-
body: schema.object({
20-
featureName: schema.string(),
21-
licenseType: schema.string({
22-
validate: (value) => {
23-
if (!(value in LICENSE_TYPE)) {
24-
return `Invalid license type: ${value}`;
25-
}
26-
},
27-
}),
28-
}),
19+
body: schema.arrayOf(
20+
schema.object({
21+
featureName: schema.string(),
22+
licenseType: schema.string({
23+
validate: (value) => {
24+
if (!(value in LICENSE_TYPE)) {
25+
return `Invalid license type: ${value}`;
26+
}
27+
},
28+
}),
29+
})
30+
),
2931
},
3032
},
3133
async (context, request, response) => {
32-
const { featureName, licenseType } = request.body;
34+
const registrations = request.body;
3335

34-
featureUsageSetup.register(featureName, licenseType as LicenseType);
36+
registrations.forEach(({ featureName, licenseType }) => {
37+
featureUsageSetup.register(featureName, licenseType as LicenseType);
38+
});
3539

3640
return response.ok({
3741
body: {

0 commit comments

Comments
 (0)