any
| |
| [id](./kibana-plugin-plugins-data-public.indexpattern.id.md) | | string
| |
| [metaFields](./kibana-plugin-plugins-data-public.indexpattern.metafields.md) | | string[]
| |
-| [routes](./kibana-plugin-plugins-data-public.indexpattern.routes.md) | | {
edit: string;
addField: string;
indexedFields: string;
scriptedFields: string;
sourceFilters: string;
}
| |
| [timeFieldName](./kibana-plugin-plugins-data-public.indexpattern.timefieldname.md) | | string | undefined
| |
| [title](./kibana-plugin-plugins-data-public.indexpattern.title.md) | | string
| |
| [type](./kibana-plugin-plugins-data-public.indexpattern.type.md) | | string
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.routes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.routes.md
deleted file mode 100644
index 81e7abd4f960..000000000000
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.routes.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [routes](./kibana-plugin-plugins-data-public.indexpattern.routes.md)
-
-## IndexPattern.routes property
-
-Signature:
-
-```typescript
-get routes(): {
- edit: string;
- addField: string;
- indexedFields: string;
- scriptedFields: string;
- sourceFilters: string;
- };
-```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterns.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterns.md
index fa97666a61b9..39c8b0a700c8 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterns.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterns.md
@@ -18,7 +18,6 @@ indexPatterns: {
validate: typeof validateIndexPattern;
getFromSavedObject: typeof getFromSavedObject;
flattenHitWrapper: typeof flattenHitWrapper;
- getRoutes: typeof getRoutes;
formatHitProvider: typeof formatHitProvider;
}
```
diff --git a/docs/user/alerting/action-types/email.asciidoc b/docs/user/alerting/action-types/email.asciidoc
index 689d870d9cad..81b4e210961f 100644
--- a/docs/user/alerting/action-types/email.asciidoc
+++ b/docs/user/alerting/action-types/email.asciidoc
@@ -24,17 +24,17 @@ Password:: password for 'login' type authentication.
[source,text]
--
- id: 'my-email'
- name: preconfigured-email-action-type
- actionTypeId: .email
- config:
- from: testsender@test.com <1.1>
- host: validhostname <1.2>
- port: 8080 <1.3>
- secure: false <1.4>
- secrets:
- user: testuser <2.1>
- password: passwordkeystorevalue <2.2>
+ my-email:
+ name: preconfigured-email-action-type
+ actionTypeId: .email
+ config:
+ from: testsender@test.com <1.1>
+ host: validhostname <1.2>
+ port: 8080 <1.3>
+ secure: false <1.4>
+ secrets:
+ user: testuser <2.1>
+ password: passwordkeystorevalue <2.2>
--
`config` defines the action type specific to the configuration and contains the following properties:
diff --git a/docs/user/alerting/action-types/index.asciidoc b/docs/user/alerting/action-types/index.asciidoc
index 4f5254e3311d..c71412210c53 100644
--- a/docs/user/alerting/action-types/index.asciidoc
+++ b/docs/user/alerting/action-types/index.asciidoc
@@ -21,13 +21,13 @@ Execution time field:: This field will be automatically set to the time the ale
[source,text]
--
- id: 'my-index'
- name: action-type-index
- actionTypeId: .index
- config:
- index: .kibana <1>
- refresh: true <2>
- executionTimeField: somedate <3>
+ my-index:
+ name: action-type-index
+ actionTypeId: .index
+ config:
+ index: .kibana <1>
+ refresh: true <2>
+ executionTimeField: somedate <3>
--
`config` defines the action type specific to the configuration and contains the following properties:
diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc
index 957c035b028f..cd51ec2e3301 100644
--- a/docs/user/alerting/action-types/pagerduty.asciidoc
+++ b/docs/user/alerting/action-types/pagerduty.asciidoc
@@ -141,13 +141,13 @@ Integration Key:: A 32 character PagerDuty Integration Key for an integration
[source,text]
--
- id: 'my-pagerduty'
- name: preconfigured-pagerduty-action-type
- actionTypeId: .pagerduty
- config:
- apiUrl: https://test.host <1.1>
- secrets:
- routingKey: testroutingkey <2.1>
+ my-pagerduty:
+ name: preconfigured-pagerduty-action-type
+ actionTypeId: .pagerduty
+ config:
+ apiUrl: https://test.host <1.1>
+ secrets:
+ routingKey: testroutingkey <2.1>
--
`config` defines the action type specific to the configuration and contains the following properties:
diff --git a/docs/user/alerting/action-types/server-log.asciidoc b/docs/user/alerting/action-types/server-log.asciidoc
index f08dbe5542f0..eadca229bc19 100644
--- a/docs/user/alerting/action-types/server-log.asciidoc
+++ b/docs/user/alerting/action-types/server-log.asciidoc
@@ -18,9 +18,9 @@ Name:: The name of the connector. The name is used to identify a connector
[source,text]
--
- id: 'my-server-log'
- name: test
- actionTypeId: .server-log
+ my-server-log:
+ name: test
+ actionTypeId: .server-log
--
[float]
diff --git a/docs/user/alerting/action-types/slack.asciidoc b/docs/user/alerting/action-types/slack.asciidoc
index 195093536bc0..afa616ba77b3 100644
--- a/docs/user/alerting/action-types/slack.asciidoc
+++ b/docs/user/alerting/action-types/slack.asciidoc
@@ -19,11 +19,11 @@ Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messa
[source,text]
--
- id: 'my-slack'
- name: preconfigured-slack-action-type
- actionTypeId: .slack
- config:
- webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz' <1>
+ my-slack:
+ name: preconfigured-slack-action-type
+ actionTypeId: .slack
+ config:
+ webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz' <1>
--
`config` defines the action type specific to the configuration and contains the following properties:
diff --git a/docs/user/alerting/action-types/webhook.asciidoc b/docs/user/alerting/action-types/webhook.asciidoc
index f4c108426642..27609652288b 100644
--- a/docs/user/alerting/action-types/webhook.asciidoc
+++ b/docs/user/alerting/action-types/webhook.asciidoc
@@ -23,17 +23,17 @@ Password:: An optional password. If set, HTTP basic authentication is used. Cur
[source,text]
--
- id: 'my-webhook'
- name: preconfigured-webhook-action-type
- actionTypeId: .webhook
- config:
- url: https://test.host <1.1>
- method: POST <1.2>
- headers: <1.3>
- testheader: testvalue
- secrets:
- user: testuser <2.1>
- password: passwordkeystorevalue <2.2>
+ my-webhook:
+ name: preconfigured-webhook-action-type
+ actionTypeId: .webhook
+ config:
+ url: https://test.host <1.1>
+ method: POST <1.2>
+ headers: <1.3>
+ testheader: testvalue
+ secrets:
+ user: testuser <2.1>
+ password: passwordkeystorevalue <2.2>
--
`config` defines the action type specific to the configuration and contains the following properties:
diff --git a/docs/user/alerting/pre-configured-connectors.asciidoc b/docs/user/alerting/pre-configured-connectors.asciidoc
index 5ff4ea15df56..d5c20d1853d4 100644
--- a/docs/user/alerting/pre-configured-connectors.asciidoc
+++ b/docs/user/alerting/pre-configured-connectors.asciidoc
@@ -25,12 +25,12 @@ The following example shows a valid configuration of two out-of-the box connecto
```js
xpack.actions.preconfigured:
- - id: 'my-slack1' <1>
+ my-slack1: <1>
actionTypeId: .slack <2>
name: 'Slack #xyz' <3>
config: <4>
webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz'
- - id: 'webhook-service'
+ webhook-service:
actionTypeId: .webhook
name: 'Email service'
config:
@@ -44,7 +44,7 @@ The following example shows a valid configuration of two out-of-the box connecto
password: changeme
```
-<1> `id` is the action connector identifier.
+<1> the key is the action connector identifier, eg `my-slack1` in this example.
<2> `actionTypeId` is the action type identifier.
<3> `name` is the name of the preconfigured connector.
<4> `config` is the action type specific to the configuration.
@@ -69,7 +69,7 @@ The following example shows a valid configuration of preconfigured action type w
```js
xpack.actions.enabledActionTypes: ['.slack', '.email', '.index'] <1>
xpack.actions.preconfigured: <2>
- - id: 'my-server-log'
+ my-server-log:
actionTypeId: .server-log
name: 'Server log #xyz'
```
diff --git a/docs/visualize/tsvb.asciidoc b/docs/visualize/tsvb.asciidoc
index 36709c2cc643..9a1e81670b65 100644
--- a/docs/visualize/tsvb.asciidoc
+++ b/docs/visualize/tsvb.asciidoc
@@ -122,3 +122,17 @@ Edit the source for the Markdown visualization.
. To insert the mustache template variable into the editor, click the variable name.
+
The http://mustache.github.io/mustache.5.html[mustache syntax] uses the Handlebar.js processor, which is an extended version of the Mustache template language.
+
+[float]
+[[tsvb-style-markdown]]
+==== Style Markdown text
+
+Style your Markdown visualization using http://lesscss.org/features/[less syntax].
+
+. Select *Markdown*.
+
+. Select *Panel options*.
+
+. Enter styling rules in *Custom CSS* section
++
+Less in TSVB does not support custom plugins or inline JavaScript.
diff --git a/package.json b/package.json
index 8a92b4648930..0c83cb429b65 100644
--- a/package.json
+++ b/package.json
@@ -210,7 +210,7 @@
"leaflet-responsive-popup": "0.6.4",
"leaflet-vega": "^0.8.6",
"leaflet.heat": "0.2.0",
- "less": "^2.7.3",
+ "less": "npm:@elastic/less@2.7.3-kibana",
"less-loader": "5.0.0",
"lodash": "npm:@elastic/lodash@3.10.1-kibana4",
"lodash.clonedeep": "^4.5.0",
diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/README.md b/packages/kbn-dev-utils/src/ci_stats_reporter/README.md
index 6133f9871699..c7b98224c4e5 100644
--- a/packages/kbn-dev-utils/src/ci_stats_reporter/README.md
+++ b/packages/kbn-dev-utils/src/ci_stats_reporter/README.md
@@ -8,7 +8,7 @@ This class integrates with the `ciStats.trackBuild {}` Jenkins Pipeline function
To create an instance of the reporter, import the class and call `CiStatsReporter.fromEnv(log)` (passing it a tooling log).
-#### `CiStatsReporter#metric(name: string, subName: string, value: number)`
+#### `CiStatsReporter#metrics(metrics: Array<{ group: string, id: string, value: number }>)`
Use this method to record metrics in the Kibana CI Stats service.
@@ -19,5 +19,11 @@ import { CiStatsReporter, ToolingLog } from '@kbn/dev-utils';
const log = new ToolingLog(...);
const reporter = CiStatsReporter.fromEnv(log)
-reporter.metric('Build speed', specificBuildName, timeToRunBuild)
+reporter.metrics([
+ {
+ group: 'Build size',
+ id: specificBuildName,
+ value: sizeOfBuild
+ }
+])
```
\ No newline at end of file
diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
index 5fe1844a8556..4e9128961043 100644
--- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
+++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
@@ -84,13 +84,16 @@ export class CiStatsReporter {
return !!this.config;
}
- async metric(name: string, subName: string, value: number) {
+ async metrics(metrics: Array<{ group: string; id: string; value: number }>) {
if (!this.config) {
return;
}
let attempt = 0;
const maxAttempts = 5;
+ const bodySummary = metrics
+ .map(({ group, id, value }) => `[${group}/${id}=${value}]`)
+ .join(' ');
while (true) {
attempt += 1;
@@ -98,18 +101,14 @@ export class CiStatsReporter {
try {
await Axios.request({
method: 'POST',
- url: '/metric',
+ url: '/v1/metrics',
baseURL: this.config.apiUrl,
- params: {
- buildId: this.config.buildId,
- },
headers: {
Authorization: `token ${this.config.apiToken}`,
},
data: {
- name,
- subName,
- value,
+ buildId: this.config.buildId,
+ metrics,
},
});
@@ -125,14 +124,14 @@ export class CiStatsReporter {
this.log.warning(
`error recording metric [status=${error.response.status}] [resp=${inspect(
error.response.data
- )}] [${name}/${subName}=${value}]`
+ )}] ${bodySummary}`
);
return;
}
if (attempt === maxAttempts) {
this.log.warning(
- `failed to reach kibana-ci-stats service too many times, unable to record metric [${name}/${subName}=${value}]`
+ `failed to reach kibana-ci-stats service too many times, unable to record metric ${bodySummary}`
);
return;
}
diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts
index e46075eff63a..a2fbe969e34d 100644
--- a/packages/kbn-optimizer/src/cli.ts
+++ b/packages/kbn-optimizer/src/cli.ts
@@ -21,7 +21,7 @@ import 'source-map-support/register';
import Path from 'path';
-import { run, REPO_ROOT, createFlagError, createFailError, CiStatsReporter } from '@kbn/dev-utils';
+import { run, REPO_ROOT, createFlagError, CiStatsReporter } from '@kbn/dev-utils';
import { logOptimizerState } from './log_optimizer_state';
import { OptimizerConfig } from './optimizer';
@@ -82,9 +82,9 @@ run(
throw createFlagError('expected --scan-dir to be a string');
}
- const reportStatsName = flags['report-stats'];
- if (reportStatsName !== undefined && typeof reportStatsName !== 'string') {
- throw createFlagError('expected --report-stats to be a string');
+ const reportStats = flags['report-stats'] ?? false;
+ if (typeof reportStats !== 'boolean') {
+ throw createFlagError('expected --report-stats to have no value');
}
const config = OptimizerConfig.create({
@@ -103,22 +103,32 @@ run(
let update$ = runOptimizer(config);
- if (reportStatsName) {
+ if (reportStats) {
const reporter = CiStatsReporter.fromEnv(log);
if (!reporter.isEnabled()) {
- throw createFailError('Unable to initialize CiStatsReporter from env');
+ log.warning('Unable to initialize CiStatsReporter from env');
}
- update$ = update$.pipe(reportOptimizerStats(reporter, reportStatsName));
+ update$ = update$.pipe(reportOptimizerStats(reporter, config));
}
await update$.pipe(logOptimizerState(log, config)).toPromise();
},
{
flags: {
- boolean: ['core', 'watch', 'oss', 'examples', 'dist', 'cache', 'profile', 'inspect-workers'],
- string: ['workers', 'scan-dir', 'report-stats'],
+ boolean: [
+ 'core',
+ 'watch',
+ 'oss',
+ 'examples',
+ 'dist',
+ 'cache',
+ 'profile',
+ 'inspect-workers',
+ 'report-stats',
+ ],
+ string: ['workers', 'scan-dir'],
default: {
core: true,
examples: true,
@@ -136,7 +146,7 @@ run(
--dist create bundles that are suitable for inclusion in the Kibana distributable
--scan-dir add a directory to the list of directories scanned for plugins (specify as many times as necessary)
--no-inspect-workers when inspecting the parent process, don't inspect the workers
- --report-stats=[name] attempt to report stats about this execution of the build to the kibana-ci-stats service using this name
+ --report-stats attempt to report stats about this execution of the build to the kibana-ci-stats service using this name
`,
},
}
diff --git a/packages/kbn-optimizer/src/report_optimizer_stats.ts b/packages/kbn-optimizer/src/report_optimizer_stats.ts
index 375978b9b794..06161fb2567b 100644
--- a/packages/kbn-optimizer/src/report_optimizer_stats.ts
+++ b/packages/kbn-optimizer/src/report_optimizer_stats.ts
@@ -21,10 +21,10 @@ import { materialize, mergeMap, dematerialize } from 'rxjs/operators';
import { CiStatsReporter } from '@kbn/dev-utils';
import { OptimizerUpdate$ } from './run_optimizer';
-import { OptimizerState } from './optimizer';
+import { OptimizerState, OptimizerConfig } from './optimizer';
import { pipeClosure } from './common';
-export function reportOptimizerStats(reporter: CiStatsReporter, name: string) {
+export function reportOptimizerStats(reporter: CiStatsReporter, config: OptimizerConfig) {
return pipeClosure((update$: OptimizerUpdate$) => {
let lastState: OptimizerState | undefined;
return update$.pipe(
@@ -35,7 +35,18 @@ export function reportOptimizerStats(reporter: CiStatsReporter, name: string) {
}
if (n.kind === 'C' && lastState) {
- await reporter.metric('@kbn/optimizer build time', name, lastState.durSec);
+ await reporter.metrics(
+ config.bundles.map(bundle => {
+ // make the cache read from the cache file since it was likely updated by the worker
+ bundle.cache.refresh();
+
+ return {
+ group: `@kbn/optimizer bundle module count`,
+ id: bundle.id,
+ value: bundle.cache.getModuleCount() || 0,
+ };
+ })
+ );
}
return n;
diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts
index 95e826e7620a..49bcc6e7e704 100644
--- a/packages/kbn-optimizer/src/worker/webpack.config.ts
+++ b/packages/kbn-optimizer/src/worker/webpack.config.ts
@@ -137,9 +137,9 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) {
// or which have require() statements that should be ignored because the file is
// already bundled with all its necessary depedencies
noParse: [
- /[\///]node_modules[\///]elasticsearch-browser[\///]/,
- /[\///]node_modules[\///]lodash[\///]index\.js$/,
- /[\///]node_modules[\///]vega-lib[\///]build[\///]vega\.js$/,
+ /[\/\\]node_modules[\/\\]elasticsearch-browser[\/\\]/,
+ /[\/\\]node_modules[\/\\]lodash[\/\\]index\.js$/,
+ /[\/\\]node_modules[\/\\]vega-lib[\/\\]build[\/\\]vega\.js$/,
],
rules: [
diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js
index 28cf36dedba3..1b70cced4a5c 100644
--- a/packages/kbn-pm/dist/index.js
+++ b/packages/kbn-pm/dist/index.js
@@ -43933,30 +43933,29 @@ class CiStatsReporter {
isEnabled() {
return !!this.config;
}
- async metric(name, subName, value) {
+ async metrics(metrics) {
var _a, _b, _c, _d;
if (!this.config) {
return;
}
let attempt = 0;
const maxAttempts = 5;
+ const bodySummary = metrics
+ .map(({ group, id, value }) => `[${group}/${id}=${value}]`)
+ .join(' ');
while (true) {
attempt += 1;
try {
await axios_1.default.request({
method: 'POST',
- url: '/metric',
+ url: '/v1/metrics',
baseURL: this.config.apiUrl,
- params: {
- buildId: this.config.buildId,
- },
headers: {
Authorization: `token ${this.config.apiToken}`,
},
data: {
- name,
- subName,
- value,
+ buildId: this.config.buildId,
+ metrics,
},
});
return;
@@ -43968,11 +43967,11 @@ class CiStatsReporter {
}
if (((_b = error) === null || _b === void 0 ? void 0 : _b.response) && error.response.status !== 502) {
// error response from service was received so warn the user and move on
- this.log.warning(`error recording metric [status=${error.response.status}] [resp=${util_1.inspect(error.response.data)}] [${name}/${subName}=${value}]`);
+ this.log.warning(`error recording metric [status=${error.response.status}] [resp=${util_1.inspect(error.response.data)}] ${bodySummary}`);
return;
}
if (attempt === maxAttempts) {
- this.log.warning(`failed to reach kibana-ci-stats service too many times, unable to record metric [${name}/${subName}=${value}]`);
+ this.log.warning(`failed to reach kibana-ci-stats service too many times, unable to record metric ${bodySummary}`);
return;
}
// we failed to reach the backend and we have remaining attempts, lets retry after a short delay
diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx
index 8442f1ecc641..fd496da26283 100644
--- a/src/core/public/application/application_service.tsx
+++ b/src/core/public/application/application_service.tsx
@@ -114,7 +114,9 @@ export class ApplicationService {
context,
http: { basePath },
injectedMetadata,
- redirectTo = (path: string) => (window.location.href = path),
+ redirectTo = (path: string) => {
+ window.location.assign(path);
+ },
history,
}: SetupDeps): InternalApplicationSetup {
const basename = basePath.get();
@@ -210,7 +212,10 @@ export class ApplicationService {
}
const appBasePath = basePath.prepend(appRoute);
- const mount: LegacyAppMounter = () => redirectTo(appBasePath);
+ const mount: LegacyAppMounter = ({ history: appHistory }) => {
+ redirectTo(appHistory.createHref(appHistory.location));
+ window.location.reload();
+ };
const { updater$, ...appProps } = app;
this.apps.set(app.id, {
diff --git a/src/core/public/application/integration_tests/application_service.test.tsx b/src/core/public/application/integration_tests/application_service.test.tsx
index 60c36d3e330e..e399fbc72697 100644
--- a/src/core/public/application/integration_tests/application_service.test.tsx
+++ b/src/core/public/application/integration_tests/application_service.test.tsx
@@ -18,8 +18,10 @@
*/
import { take } from 'rxjs/operators';
-import { createRenderer } from './utils';
+import { act } from 'react-dom/test-utils';
import { createMemoryHistory, MemoryHistory } from 'history';
+
+import { createRenderer } from './utils';
import { ApplicationService } from '../application_service';
import { httpServiceMock } from '../../http/http_service.mock';
import { contextServiceMock } from '../../context/context_service.mock';
@@ -27,6 +29,9 @@ import { injectedMetadataServiceMock } from '../../injected_metadata/injected_me
import { MockLifecycle } from '../test_types';
import { overlayServiceMock } from '../../overlays/overlay_service.mock';
import { AppMountParameters } from '../types';
+import { ScopedHistory } from '../scoped_history';
+
+const flushPromises = () => new Promise(resolve => setImmediate(resolve));
describe('ApplicationService', () => {
let setupDeps: MockLifecycle<'setup'>;
@@ -83,7 +88,10 @@ describe('ApplicationService', () => {
expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1');
- resolveMount!();
+ await act(async () => {
+ resolveMount!();
+ await flushPromises();
+ });
expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1');
});
@@ -109,7 +117,7 @@ describe('ApplicationService', () => {
const { navigateToApp, currentAppId$ } = await service.start(startDeps);
- await navigateToApp('app1');
+ await act(() => navigateToApp('app1'));
expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1');
@@ -120,6 +128,46 @@ describe('ApplicationService', () => {
});
});
+ it('redirects to full path when navigating to legacy app', async () => {
+ const redirectTo = jest.fn();
+ const reloadSpy = jest.spyOn(window.location, 'reload').mockImplementation(() => {});
+
+ // In the real application, we use a BrowserHistory instance configured with `basename`. However, in tests we must
+ // use MemoryHistory which does not support `basename`. In order to emulate this behavior, we will wrap this
+ // instance with a ScopedHistory configured with a basepath.
+ history.push(setupDeps.http.basePath.get()); // ScopedHistory constructor will fail if underlying history is not currently at basePath.
+ const { register, registerLegacyApp } = service.setup({
+ ...setupDeps,
+ redirectTo,
+ history: new ScopedHistory(history, setupDeps.http.basePath.get()),
+ });
+
+ register(Symbol(), {
+ id: 'app1',
+ title: 'App1',
+ mount: ({ onAppLeave }: AppMountParameters) => {
+ onAppLeave(actions => actions.default());
+ return () => undefined;
+ },
+ });
+ registerLegacyApp({
+ id: 'myLegacyTestApp',
+ appUrl: '/app/myLegacyTestApp',
+ title: 'My Legacy Test App',
+ });
+
+ const { navigateToApp, getComponent } = await service.start(startDeps);
+
+ update = createRenderer(getComponent());
+
+ await navigate('/test/app/app1');
+ await act(() => navigateToApp('myLegacyTestApp', { path: '#/some-path' }));
+
+ expect(redirectTo).toHaveBeenCalledWith('/test/app/myLegacyTestApp#/some-path');
+ expect(reloadSpy).toHaveBeenCalled();
+ reloadSpy.mockRestore();
+ });
+
describe('leaving an application that registered an app leave handler', () => {
it('navigates to the new app if action is default', async () => {
startDeps.overlays.openConfirm.mockResolvedValue(true);
@@ -146,8 +194,10 @@ describe('ApplicationService', () => {
update = createRenderer(getComponent());
- await navigate('/app/app1');
- await navigateToApp('app2');
+ await act(async () => {
+ await navigate('/app/app1');
+ await navigateToApp('app2');
+ });
expect(startDeps.overlays.openConfirm).not.toHaveBeenCalled();
expect(history.entries.length).toEqual(3);
@@ -179,8 +229,10 @@ describe('ApplicationService', () => {
update = createRenderer(getComponent());
- await navigate('/app/app1');
- await navigateToApp('app2');
+ await act(async () => {
+ await navigate('/app/app1');
+ await navigateToApp('app2');
+ });
expect(startDeps.overlays.openConfirm).toHaveBeenCalledTimes(1);
expect(startDeps.overlays.openConfirm).toHaveBeenCalledWith(
@@ -216,8 +268,10 @@ describe('ApplicationService', () => {
update = createRenderer(getComponent());
- await navigate('/app/app1');
- await navigateToApp('app2');
+ await act(async () => {
+ await navigate('/app/app1');
+ await navigateToApp('app2');
+ });
expect(startDeps.overlays.openConfirm).toHaveBeenCalledTimes(1);
expect(startDeps.overlays.openConfirm).toHaveBeenCalledWith(
diff --git a/src/core/server/logging/README.md b/src/core/server/logging/README.md
index ed64e7c4ce0b..553dc7c36e82 100644
--- a/src/core/server/logging/README.md
+++ b/src/core/server/logging/README.md
@@ -167,7 +167,7 @@ logging:
- context: plugins
appenders: [custom]
level: warn
- - context: plugins.pid
+ - context: plugins.myPlugin
level: info
- context: server
level: fatal
@@ -180,14 +180,14 @@ logging:
Here is what we get with the config above:
-| Context | Appenders | Level |
-| ------------- |:------------------------:| -----:|
-| root | console, file | error |
-| plugins | custom | warn |
-| plugins.pid | custom | info |
-| server | console, file | fatal |
-| optimize | console | error |
-| telemetry | json-file-appender | all |
+| Context | Appenders | Level |
+| ---------------- |:------------------------:| -----:|
+| root | console, file | error |
+| plugins | custom | warn |
+| plugins.myPlugin | custom | info |
+| server | console, file | fatal |
+| optimize | console | error |
+| telemetry | json-file-appender | all |
The `root` logger has a dedicated configuration node since this context is special and should always exist. By
@@ -259,7 +259,7 @@ define a custom one.
```yaml
logging:
loggers:
- - context: your-plugin
+ - context: plugins.myPlugin
appenders: [console]
```
Logs in a *file* if given file path. You should define a custom appender with `kind: file`
@@ -273,7 +273,7 @@ logging:
layout:
kind: pattern
loggers:
- - context: your-plugin
+ - context: plugins.myPlugin
appenders: [file]
```
#### logging.json
@@ -282,10 +282,10 @@ the output format with [layouts](#layouts).
#### logging.quiet
Suppresses all logging output other than error messages. With new logging, config can be achieved
-with adjusting minimum required [logging level](#log-level)
+with adjusting minimum required [logging level](#log-level).
```yaml
loggers:
- - context: my-plugin
+ - context: plugins.myPlugin
appenders: [console]
level: error
# or for all output
diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js
index 927171438ae9..c46fcfbc6dbd 100644
--- a/src/core/server/saved_objects/service/lib/repository.test.js
+++ b/src/core/server/saved_objects/service/lib/repository.test.js
@@ -23,6 +23,7 @@ import { SavedObjectsErrorHelpers } from './errors';
import { SavedObjectsSerializer } from '../../serialization';
import { encodeHitVersion } from '../../version';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
+import { DocumentMigrator } from '../../migrations/core/document_migrator';
jest.mock('./search_dsl/search_dsl', () => ({ getSearchDsl: jest.fn() }));
@@ -115,6 +116,7 @@ describe('SavedObjectsRepository', () => {
const createType = type => ({
name: type,
mappings: { properties: mappings.properties[type].properties },
+ migrations: { '1.1.1': doc => doc },
});
const registry = new SavedObjectTypeRegistry();
@@ -144,6 +146,13 @@ describe('SavedObjectsRepository', () => {
namespaceType: 'agnostic',
});
+ const documentMigrator = new DocumentMigrator({
+ typeRegistry: registry,
+ kibanaVersion: '2.0.0',
+ log: {},
+ validateDoc: jest.fn(),
+ });
+
const getMockGetResponse = ({ type, id, references, namespace }) => ({
// NOTE: Elasticsearch returns more fields (_index, _type) but the SavedObjectsRepository method ignores these
found: true,
@@ -207,7 +216,7 @@ describe('SavedObjectsRepository', () => {
beforeEach(() => {
callAdminCluster = jest.fn();
migrator = {
- migrateDocument: jest.fn(doc => doc),
+ migrateDocument: jest.fn().mockImplementation(documentMigrator.migrate),
runMigrations: async () => ({ status: 'skipped' }),
};
@@ -424,9 +433,17 @@ describe('SavedObjectsRepository', () => {
const getMockBulkCreateResponse = (objects, namespace) => {
return {
- items: objects.map(({ type, id }) => ({
+ items: objects.map(({ type, id, attributes, references, migrationVersion }) => ({
create: {
_id: `${namespace ? `${namespace}:` : ''}${type}:${id}`,
+ _source: {
+ [type]: attributes,
+ type,
+ namespace,
+ references,
+ ...mockTimestampFields,
+ migrationVersion: migrationVersion || { [type]: '1.1.1' },
+ },
...mockVersionProps,
},
})),
@@ -474,7 +491,7 @@ describe('SavedObjectsRepository', () => {
const expectSuccessResult = obj => ({
...obj,
- migrationVersion: undefined,
+ migrationVersion: { [obj.type]: '1.1.1' },
version: mockVersion,
...mockTimestampFields,
});
@@ -619,13 +636,16 @@ describe('SavedObjectsRepository', () => {
};
const bulkCreateError = async (obj, esError, expectedError) => {
- const objects = [obj1, obj, obj2];
- const response = getMockBulkCreateResponse(objects);
+ let response;
if (esError) {
+ response = getMockBulkCreateResponse([obj1, obj, obj2]);
response.items[1].create = { error: esError };
+ } else {
+ response = getMockBulkCreateResponse([obj1, obj2]);
}
callAdminCluster.mockResolvedValue(response); // this._writeToCluster('bulk', ...)
+ const objects = [obj1, obj, obj2];
const result = await savedObjectsRepository.bulkCreate(objects);
expectClusterCalls('bulk');
const objCall = esError ? expectObjArgs(obj) : [];
@@ -781,7 +801,7 @@ describe('SavedObjectsRepository', () => {
id: 'three',
};
const objects = [obj1, obj, obj2];
- const response = getMockBulkCreateResponse(objects);
+ const response = getMockBulkCreateResponse([obj1, obj2]);
callAdminCluster.mockResolvedValue(response); // this._writeToCluster('bulk', ...)
const result = await savedObjectsRepository.bulkCreate(objects);
expect(callAdminCluster).toHaveBeenCalledTimes(1);
@@ -789,6 +809,32 @@ describe('SavedObjectsRepository', () => {
saved_objects: [expectSuccessResult(obj1), expectError(obj), expectSuccessResult(obj2)],
});
});
+
+ it(`a deserialized saved object`, async () => {
+ // Test for fix to https://github.com/elastic/kibana/issues/65088 where
+ // we returned raw ID's when an object without an id was created.
+ const namespace = 'myspace';
+ const response = getMockBulkCreateResponse([obj1, obj2], namespace);
+ callAdminCluster.mockResolvedValueOnce(response); // this._writeToCluster('bulk', ...)
+
+ // Bulk create one object with id unspecified, and one with id specified
+ const result = await savedObjectsRepository.bulkCreate([{ ...obj1, id: undefined }, obj2], {
+ namespace,
+ });
+
+ // Assert that both raw docs from the ES response are deserialized
+ expect(serializer.rawToSavedObject).toHaveBeenNthCalledWith(1, {
+ ...response.items[0].create,
+ _id: expect.stringMatching(/^myspace:config:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/),
+ });
+ expect(serializer.rawToSavedObject).toHaveBeenNthCalledWith(2, response.items[1].create);
+
+ // Assert that ID's are deserialized to remove the type and namespace
+ expect(result.saved_objects[0].id).toEqual(
+ expect.stringMatching(/^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/)
+ );
+ expect(result.saved_objects[1].id).toEqual(obj2.id);
+ });
});
});
@@ -1604,6 +1650,7 @@ describe('SavedObjectsRepository', () => {
version: mockVersion,
attributes,
references,
+ migrationVersion: { [type]: '1.1.1' },
});
});
});
diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts
index bc8ad2cdb005..61027130e0eb 100644
--- a/src/core/server/saved_objects/service/lib/repository.ts
+++ b/src/core/server/saved_objects/service/lib/repository.ts
@@ -18,6 +18,7 @@
*/
import { omit } from 'lodash';
+import uuid from 'uuid';
import { retryCallCluster } from '../../../elasticsearch/retry_call_cluster';
import { APICaller } from '../../../elasticsearch/';
@@ -299,6 +300,8 @@ export class SavedObjectsRepository {
const requiresNamespacesCheck =
method === 'index' && this._registry.isMultiNamespace(object.type);
+ if (object.id == null) object.id = uuid.v1();
+
return {
tag: 'Right' as 'Right',
value: {
@@ -404,35 +407,25 @@ export class SavedObjectsRepository {
}
const { requestedId, rawMigratedDoc, esRequestIndex } = expectedResult.value;
- const response = bulkResponse.items[esRequestIndex];
- const {
- error,
- _id: responseId,
- _seq_no: seqNo,
- _primary_term: primaryTerm,
- } = Object.values(response)[0] as any;
-
- const {
- _source: { type, [type]: attributes, references = [], namespaces },
- } = rawMigratedDoc;
-
- const id = requestedId || responseId;
+ const { error, ...rawResponse } = Object.values(
+ bulkResponse.items[esRequestIndex]
+ )[0] as any;
+
if (error) {
return {
- id,
- type,
- error: getBulkOperationError(error, type, id),
+ id: requestedId,
+ type: rawMigratedDoc._source.type,
+ error: getBulkOperationError(error, rawMigratedDoc._source.type, requestedId),
};
}
- return {
- id,
- type,
- ...(namespaces && { namespaces }),
- updated_at: time,
- version: encodeVersion(seqNo, primaryTerm),
- attributes,
- references,
- };
+
+ // When method == 'index' the bulkResponse doesn't include the indexed
+ // _source so we return rawMigratedDoc but have to spread the latest
+ // _seq_no and _primary_term values from the rawResponse.
+ return this._serializer.rawToSavedObject({
+ ...rawMigratedDoc,
+ ...{ _seq_no: rawResponse._seq_no, _primary_term: rawResponse._primary_term },
+ });
}),
};
}
diff --git a/src/dev/build/tasks/build_kibana_platform_plugins.js b/src/dev/build/tasks/build_kibana_platform_plugins.js
index 28d6b49f9e89..153a3120f896 100644
--- a/src/dev/build/tasks/build_kibana_platform_plugins.js
+++ b/src/dev/build/tasks/build_kibana_platform_plugins.js
@@ -39,11 +39,10 @@ export const BuildKibanaPlatformPluginsTask = {
});
const reporter = CiStatsReporter.fromEnv(log);
- const reportStatsName = build.isOss() ? 'oss distributable' : 'default distributable';
await runOptimizer(optimizerConfig)
.pipe(
- reportOptimizerStats(reporter, reportStatsName),
+ reportOptimizerStats(reporter, optimizerConfig),
logOptimizerState(log, optimizerConfig)
)
.toPromise();
diff --git a/src/dev/build/tasks/create_archives_task.js b/src/dev/build/tasks/create_archives_task.js
index 06be1bd0bd14..541b9551dbc9 100644
--- a/src/dev/build/tasks/create_archives_task.js
+++ b/src/dev/build/tasks/create_archives_task.js
@@ -17,13 +17,22 @@
* under the License.
*/
-import path from 'path';
+import Path from 'path';
+import Fs from 'fs';
+import { promisify } from 'util';
+
+import { CiStatsReporter } from '@kbn/dev-utils';
+
import { mkdirp, compress } from '../lib';
+const asyncStat = promisify(Fs.stat);
+
export const CreateArchivesTask = {
description: 'Creating the archives for each platform',
async run(config, log, build) {
+ const archives = [];
+
// archive one at a time, parallel causes OOM sometimes
for (const platform of config.getTargetPlatforms()) {
const source = build.resolvePathForPlatform(platform, '.');
@@ -31,10 +40,15 @@ export const CreateArchivesTask = {
log.info('archiving', source, 'to', destination);
- await mkdirp(path.dirname(destination));
+ await mkdirp(Path.dirname(destination));
- switch (path.extname(destination)) {
+ switch (Path.extname(destination)) {
case '.zip':
+ archives.push({
+ format: 'zip',
+ path: destination,
+ });
+
await compress(
'zip',
{
@@ -51,6 +65,11 @@ export const CreateArchivesTask = {
break;
case '.gz':
+ archives.push({
+ format: 'tar',
+ path: destination,
+ });
+
await compress(
'tar',
{
@@ -71,5 +90,20 @@ export const CreateArchivesTask = {
throw new Error(`Unexpected extension for archive destination: ${destination}`);
}
}
+
+ const reporter = CiStatsReporter.fromEnv(log);
+ if (reporter.isEnabled()) {
+ await reporter.metrics(
+ await Promise.all(
+ archives.map(async ({ format, path }) => {
+ return {
+ group: `${build.isOss() ? 'oss ' : ''}distributable size`,
+ id: format,
+ value: (await asyncStat(path)).size,
+ };
+ })
+ )
+ );
+ }
},
};
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/utils.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/utils.ts
index bdb1436c37ef..83335a6fabfe 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/utils.ts
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/utils.ts
@@ -96,18 +96,21 @@ export function getTabs(
tabs.push({
name: getTitle('indexed', filteredCount, totalCount),
id: TAB_INDEXED_FIELDS,
+ 'data-test-subj': 'tab-indexedFields',
});
if (indexPatternListProvider.areScriptedFieldsEnabled(indexPattern)) {
tabs.push({
name: getTitle('scripted', filteredCount, totalCount),
id: TAB_SCRIPTED_FIELDS,
+ 'data-test-subj': 'tab-scriptedFields',
});
}
tabs.push({
name: getTitle('sourceFilters', filteredCount, totalCount),
id: TAB_SOURCE_FILTERS,
+ 'data-test-subj': 'tab-sourceFilters',
});
return tabs;
diff --git a/src/legacy/core_plugins/region_map/index.ts b/src/legacy/core_plugins/region_map/index.ts
deleted file mode 100644
index 8c059314786b..000000000000
--- a/src/legacy/core_plugins/region_map/index.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { resolve } from 'path';
-import { Legacy } from 'kibana';
-
-import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types';
-
-const regionMapPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) =>
- new Plugin({
- id: 'region_map',
- require: ['kibana', 'elasticsearch'],
- publicDir: resolve(__dirname, 'public'),
- uiExports: {
- hacks: [resolve(__dirname, 'public/legacy')],
- injectDefaultVars(server) {
- const { regionmap } = server.config().get('map');
-
- return {
- regionmap,
- };
- },
- },
- init: (server: Legacy.Server) => ({}),
- config(Joi: any) {
- return Joi.object({
- enabled: Joi.boolean().default(true),
- }).default();
- },
- } as Legacy.PluginSpecOptions);
-
-// eslint-disable-next-line import/no-default-export
-export default regionMapPluginInitializer;
diff --git a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
index 1bc85fa110ca..698c124d2d80 100644
--- a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
+++ b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
@@ -301,7 +301,7 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = `
>
{{screenTitle}}
-