Skip to content

Commit c54458f

Browse files
committed
Merge branch 'master' into alerts/action-groups-as-conditions
* master: Migrate `/translations` route to core (elastic#83280) [APM] Ensure APM jest script can run (elastic#83398) [Uptime] Monitor status alert use url as instance (elastic#81736) [ML] Add basic license test run details to ML+Transform READMEs (elastic#83259) TSVB doesn't communicate it's index-patterns to dashboard (elastic#82964) [Alerting UI] Added ability to assign alert actions to resolved action group in UI (elastic#83139) Skips Vega test skip flaky suite (elastic#79967) [bundle optimization] Update to semver 7.x to get tree-shaking (elastic#83020) Added ability to fire actions when an alert instance is resolved (elastic#82799) [ML] Adds functional tests for the index data visualizer card contents (elastic#83174)
2 parents f809c8b + d1abc86 commit c54458f

File tree

81 files changed

+3835
-1328
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+3835
-1328
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@
295295
"rison-node": "1.0.2",
296296
"rxjs": "^6.5.5",
297297
"seedrandom": "^3.0.5",
298-
"semver": "^5.7.0",
298+
"semver": "^7.3.2",
299299
"set-value": "^3.0.2",
300300
"source-map-support": "^0.5.19",
301301
"squel": "^5.13.0",
@@ -536,7 +536,7 @@
536536
"@types/request": "^2.48.2",
537537
"@types/seedrandom": ">=2.0.0 <4.0.0",
538538
"@types/selenium-webdriver": "^4.0.9",
539-
"@types/semver": "^5.5.0",
539+
"@types/semver": "^7",
540540
"@types/set-value": "^2.0.0",
541541
"@types/sinon": "^7.0.13",
542542
"@types/source-map-support": "^0.5.3",

packages/kbn-pm/dist/index.js

Lines changed: 2498 additions & 1009 deletions
Large diffs are not rendered by default.

src/core/server/i18n/i18n_service.test.mocks.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,8 @@ export const initTranslationsMock = jest.fn();
2626
jest.doMock('./init_translations', () => ({
2727
initTranslations: initTranslationsMock,
2828
}));
29+
30+
export const registerRoutesMock = jest.fn();
31+
jest.doMock('./routes', () => ({
32+
registerRoutes: registerRoutesMock,
33+
}));

src/core/server/i18n/i18n_service.test.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@
1717
* under the License.
1818
*/
1919

20-
import { getKibanaTranslationFilesMock, initTranslationsMock } from './i18n_service.test.mocks';
20+
import {
21+
getKibanaTranslationFilesMock,
22+
initTranslationsMock,
23+
registerRoutesMock,
24+
} from './i18n_service.test.mocks';
2125

2226
import { BehaviorSubject } from 'rxjs';
2327
import { I18nService } from './i18n_service';
2428

2529
import { configServiceMock } from '../config/mocks';
2630
import { mockCoreContext } from '../core_context.mock';
31+
import { httpServiceMock } from '../http/http_service.mock';
2732

2833
const getConfigService = (locale = 'en') => {
2934
const configService = configServiceMock.create();
@@ -41,21 +46,24 @@ const getConfigService = (locale = 'en') => {
4146
describe('I18nService', () => {
4247
let service: I18nService;
4348
let configService: ReturnType<typeof configServiceMock.create>;
49+
let http: ReturnType<typeof httpServiceMock.createInternalSetupContract>;
4450

4551
beforeEach(() => {
4652
jest.clearAllMocks();
4753
configService = getConfigService();
4854

4955
const coreContext = mockCoreContext.create({ configService });
5056
service = new I18nService(coreContext);
57+
58+
http = httpServiceMock.createInternalSetupContract();
5159
});
5260

5361
describe('#setup', () => {
5462
it('calls `getKibanaTranslationFiles` with the correct parameters', async () => {
5563
getKibanaTranslationFilesMock.mockResolvedValue([]);
5664

5765
const pluginPaths = ['/pathA', '/pathB'];
58-
await service.setup({ pluginPaths });
66+
await service.setup({ pluginPaths, http });
5967

6068
expect(getKibanaTranslationFilesMock).toHaveBeenCalledTimes(1);
6169
expect(getKibanaTranslationFilesMock).toHaveBeenCalledWith('en', pluginPaths);
@@ -65,17 +73,27 @@ describe('I18nService', () => {
6573
const translationFiles = ['/path/to/file', 'path/to/another/file'];
6674
getKibanaTranslationFilesMock.mockResolvedValue(translationFiles);
6775

68-
await service.setup({ pluginPaths: [] });
76+
await service.setup({ pluginPaths: [], http });
6977

7078
expect(initTranslationsMock).toHaveBeenCalledTimes(1);
7179
expect(initTranslationsMock).toHaveBeenCalledWith('en', translationFiles);
7280
});
7381

82+
it('calls `registerRoutesMock` with the correct parameters', async () => {
83+
await service.setup({ pluginPaths: [], http });
84+
85+
expect(registerRoutesMock).toHaveBeenCalledTimes(1);
86+
expect(registerRoutesMock).toHaveBeenCalledWith({
87+
locale: 'en',
88+
router: expect.any(Object),
89+
});
90+
});
91+
7492
it('returns accessors for locale and translation files', async () => {
7593
const translationFiles = ['/path/to/file', 'path/to/another/file'];
7694
getKibanaTranslationFilesMock.mockResolvedValue(translationFiles);
7795

78-
const { getLocale, getTranslationFiles } = await service.setup({ pluginPaths: [] });
96+
const { getLocale, getTranslationFiles } = await service.setup({ pluginPaths: [], http });
7997

8098
expect(getLocale()).toEqual('en');
8199
expect(getTranslationFiles()).toEqual(translationFiles);

src/core/server/i18n/i18n_service.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ import { take } from 'rxjs/operators';
2121
import { Logger } from '../logging';
2222
import { IConfigService } from '../config';
2323
import { CoreContext } from '../core_context';
24+
import { InternalHttpServiceSetup } from '../http';
2425
import { config as i18nConfigDef, I18nConfigType } from './i18n_config';
2526
import { getKibanaTranslationFiles } from './get_kibana_translation_files';
2627
import { initTranslations } from './init_translations';
28+
import { registerRoutes } from './routes';
2729

2830
interface SetupDeps {
31+
http: InternalHttpServiceSetup;
2932
pluginPaths: string[];
3033
}
3134

@@ -53,7 +56,7 @@ export class I18nService {
5356
this.configService = coreContext.configService;
5457
}
5558

56-
public async setup({ pluginPaths }: SetupDeps): Promise<I18nServiceSetup> {
59+
public async setup({ pluginPaths, http }: SetupDeps): Promise<I18nServiceSetup> {
5760
const i18nConfig = await this.configService
5861
.atPath<I18nConfigType>(i18nConfigDef.path)
5962
.pipe(take(1))
@@ -67,6 +70,9 @@ export class I18nService {
6770
this.log.debug(`Using translation files: [${translationFiles.join(', ')}]`);
6871
await initTranslations(locale, translationFiles);
6972

73+
const router = http.createRouter('');
74+
registerRoutes({ router, locale });
75+
7076
return {
7177
getLocale: () => locale,
7278
getTranslationFiles: () => translationFiles,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { IRouter } from '../../http';
21+
import { registerTranslationsRoute } from './translations';
22+
23+
export const registerRoutes = ({ router, locale }: { router: IRouter; locale: string }) => {
24+
registerTranslationsRoute(router, locale);
25+
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { createHash } from 'crypto';
21+
import { i18n } from '@kbn/i18n';
22+
import { schema } from '@kbn/config-schema';
23+
import { IRouter } from '../../http';
24+
25+
interface TranslationCache {
26+
translations: string;
27+
hash: string;
28+
}
29+
30+
export const registerTranslationsRoute = (router: IRouter, locale: string) => {
31+
let translationCache: TranslationCache;
32+
33+
router.get(
34+
{
35+
path: '/translations/{locale}.json',
36+
validate: {
37+
params: schema.object({
38+
locale: schema.string(),
39+
}),
40+
},
41+
options: {
42+
authRequired: false,
43+
},
44+
},
45+
(ctx, req, res) => {
46+
if (req.params.locale.toLowerCase() !== locale.toLowerCase()) {
47+
return res.notFound({
48+
body: `Unknown locale: ${req.params.locale}`,
49+
});
50+
}
51+
if (!translationCache) {
52+
const translations = JSON.stringify(i18n.getTranslation());
53+
const hash = createHash('sha1').update(translations).digest('hex');
54+
translationCache = {
55+
translations,
56+
hash,
57+
};
58+
}
59+
return res.ok({
60+
headers: {
61+
'content-type': 'application/json',
62+
'cache-control': 'must-revalidate',
63+
etag: translationCache.hash,
64+
},
65+
body: translationCache.translations,
66+
});
67+
}
68+
);
69+
};

src/core/server/server.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,6 @@ export class Server {
131131
await ensureValidConfiguration(this.configService, legacyConfigSetup);
132132
}
133133

134-
// setup i18n prior to any other service, to have translations ready
135-
const i18nServiceSetup = await this.i18n.setup({ pluginPaths });
136-
137134
const contextServiceSetup = this.context.setup({
138135
// We inject a fake "legacy plugin" with dependencies on every plugin so that legacy plugins:
139136
// 1) Can access context from any KP plugin
@@ -149,6 +146,9 @@ export class Server {
149146
context: contextServiceSetup,
150147
});
151148

149+
// setup i18n prior to any other service, to have translations ready
150+
const i18nServiceSetup = await this.i18n.setup({ http: httpSetup, pluginPaths });
151+
152152
const capabilitiesSetup = this.capabilities.setup({ http: httpSetup });
153153

154154
const elasticsearchServiceSetup = await this.elasticsearch.setup({

src/legacy/ui/ui_render/ui_render_mixin.js

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
* under the License.
1818
*/
1919

20-
import { createHash } from 'crypto';
2120
import Boom from '@hapi/boom';
22-
import { i18n } from '@kbn/i18n';
2321
import * as UiSharedDeps from '@kbn/ui-shared-deps';
2422
import { KibanaRequest } from '../../../core/server';
2523
import { AppBootstrap } from './bootstrap';
@@ -37,36 +35,6 @@ import { getApmConfig } from '../apm';
3735
* @param {KbnServer['config']} config
3836
*/
3937
export function uiRenderMixin(kbnServer, server, config) {
40-
const translationsCache = { translations: null, hash: null };
41-
server.route({
42-
path: '/translations/{locale}.json',
43-
method: 'GET',
44-
config: { auth: false },
45-
handler(request, h) {
46-
// Kibana server loads translations only for a single locale
47-
// that is specified in `i18n.locale` config value.
48-
const { locale } = request.params;
49-
if (i18n.getLocale() !== locale.toLowerCase()) {
50-
throw Boom.notFound(`Unknown locale: ${locale}`);
51-
}
52-
53-
// Stringifying thousands of labels and calculating hash on the resulting
54-
// string can be expensive so it makes sense to do it once and cache.
55-
if (translationsCache.translations == null) {
56-
translationsCache.translations = JSON.stringify(i18n.getTranslation());
57-
translationsCache.hash = createHash('sha1')
58-
.update(translationsCache.translations)
59-
.digest('hex');
60-
}
61-
62-
return h
63-
.response(translationsCache.translations)
64-
.header('cache-control', 'must-revalidate')
65-
.header('content-type', 'application/json')
66-
.etag(translationsCache.hash);
67-
},
68-
});
69-
7038
const authEnabled = !!server.auth.settings.default;
7139
server.route({
7240
path: '/bootstrap.js',

src/plugins/dashboard/common/migrate_to_730_panels.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* under the License.
1818
*/
1919
import { i18n } from '@kbn/i18n';
20-
import semver from 'semver';
20+
import semverSatisfies from 'semver/functions/satisfies';
2121
import uuid from 'uuid';
2222
import {
2323
GridData,
@@ -60,23 +60,23 @@ function isPre61Panel(
6060
}
6161

6262
function is61Panel(panel: unknown | RawSavedDashboardPanel610): panel is RawSavedDashboardPanel610 {
63-
return semver.satisfies((panel as RawSavedDashboardPanel610).version, '6.1.x');
63+
return semverSatisfies((panel as RawSavedDashboardPanel610).version, '6.1.x');
6464
}
6565

6666
function is62Panel(panel: unknown | RawSavedDashboardPanel620): panel is RawSavedDashboardPanel620 {
67-
return semver.satisfies((panel as RawSavedDashboardPanel620).version, '6.2.x');
67+
return semverSatisfies((panel as RawSavedDashboardPanel620).version, '6.2.x');
6868
}
6969

7070
function is63Panel(panel: unknown | RawSavedDashboardPanel630): panel is RawSavedDashboardPanel630 {
71-
return semver.satisfies((panel as RawSavedDashboardPanel630).version, '6.3.x');
71+
return semverSatisfies((panel as RawSavedDashboardPanel630).version, '6.3.x');
7272
}
7373

7474
function is640To720Panel(
7575
panel: unknown | RawSavedDashboardPanel640To720
7676
): panel is RawSavedDashboardPanel640To720 {
7777
return (
78-
semver.satisfies((panel as RawSavedDashboardPanel630).version, '>6.3') &&
79-
semver.satisfies((panel as RawSavedDashboardPanel630).version, '<7.3')
78+
semverSatisfies((panel as RawSavedDashboardPanel630).version, '>6.3') &&
79+
semverSatisfies((panel as RawSavedDashboardPanel630).version, '<7.3')
8080
);
8181
}
8282

0 commit comments

Comments
 (0)