mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Merge remote-tracking branch 'origin/master' into feature/merge-code
This commit is contained in:
commit
6a1fc64c39
392 changed files with 13924 additions and 5739 deletions
|
@ -7,6 +7,7 @@ source src/dev/ci_setup/setup.sh;
|
|||
|
||||
# download es snapshots
|
||||
node scripts/es snapshot --download-only;
|
||||
node scripts/es snapshot --license=oss --download-only;
|
||||
|
||||
# download reporting browsers
|
||||
cd "x-pack";
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"xpack.infra": "x-pack/plugins/infra",
|
||||
"xpack.kueryAutocomplete": "x-pack/plugins/kuery_autocomplete",
|
||||
"xpack.licenseMgmt": "x-pack/plugins/license_management",
|
||||
"xpack.maps": "x-pack/plugins/maps",
|
||||
"xpack.ml": "x-pack/plugins/ml",
|
||||
"xpack.logstash": "x-pack/plugins/logstash",
|
||||
"xpack.main": "x-pack/plugins/xpack_main",
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
# default to `true` starting in Kibana 7.0.
|
||||
#server.rewriteBasePath: false
|
||||
|
||||
# Specifies the default route when opening Kibana. You can use this setting to modify
|
||||
# the landing page when opening Kibana.
|
||||
#server.defaultRoute: /app/kibana
|
||||
|
||||
# The maximum payload size in bytes for incoming server requests.
|
||||
#server.maxPayloadBytes: 1048576
|
||||
|
||||
|
@ -36,9 +40,6 @@
|
|||
# dashboards. Kibana creates a new index if the index doesn't already exist.
|
||||
#kibana.index: ".kibana"
|
||||
|
||||
# The default application to load.
|
||||
#kibana.defaultAppId: "home"
|
||||
|
||||
# If your Elasticsearch is protected with basic authentication, these settings provide
|
||||
# the username and password that the Kibana server uses to perform maintenance on the Kibana
|
||||
# index at startup. Your Kibana users still need to authenticate with Elasticsearch, which
|
||||
|
|
|
@ -20,7 +20,7 @@ Many Kibana developers hang out on `irc.freenode.net` in the `#kibana` channel.
|
|||
[float]
|
||||
==== Plugin Generator
|
||||
|
||||
It is recommended that you kick-start your plugin by generating it with the {repo}tree/{branch}/packages/kbn-plugin-generator[Kibana Plugin Generator]. Run the following within the Kibana repo and you will be asked a couple questions, see some progress bars, and have a freshly generated plugin ready for you to play within Kibana's sibling `kibana-extra` folder.
|
||||
It is recommended that you kick-start your plugin by generating it with the {repo}tree/{branch}/packages/kbn-plugin-generator[Kibana Plugin Generator]. Run the following within the Kibana repo and you will be asked a couple questions, see some progress bars, and have a freshly generated plugin ready for you to play within Kibana's `plugins` folder.
|
||||
|
||||
["source","shell"]
|
||||
-----------
|
||||
|
@ -31,14 +31,15 @@ node scripts/generate_plugin my_plugin_name # replace "my_plugin_name" with your
|
|||
[float]
|
||||
==== Directory structure for plugins
|
||||
|
||||
The Kibana directory must be named `kibana`, and your plugin directory must be located within the sibling `kibana-extra` folder, for example:
|
||||
The Kibana directory must be named `kibana`, and your plugin directory should be located in the root of `kibana` in a `plugins` directory, for example:
|
||||
|
||||
["source","shell"]
|
||||
-----------
|
||||
.
|
||||
├── kibana
|
||||
├── kibana-extra/foo-plugin
|
||||
└── kibana-extra/bar-plugin
|
||||
└── kibana
|
||||
└── plugins
|
||||
├── foo-plugin
|
||||
└── bar-plugin
|
||||
-----------
|
||||
|
||||
[float]
|
||||
|
|
BIN
docs/images/management-upgrade-assistant-9.0.png
Executable file
BIN
docs/images/management-upgrade-assistant-9.0.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 326 KiB |
|
@ -1,14 +1,47 @@
|
|||
[[upgrade-assistant]]
|
||||
== Upgrade Assistant
|
||||
|
||||
The Upgrade Assistant helps you prepare for your upgrade to {es} 8.0.
|
||||
To access the assistant, go to *Management > 8.0 Upgrade Assistant*.
|
||||
The Upgrade Assistant helps you prepare for your upgrade to {es} 9.0.
|
||||
To access the assistant, go to *Management > 9.0 Upgrade Assistant*.
|
||||
|
||||
The assistant identifies the deprecated settings in your cluster and indices
|
||||
and guides you through the process of resolving issues, including reindexing.
|
||||
|
||||
Before upgrading to Elasticsearch 8.0, make sure that you are using the final
|
||||
7.x minor release to see the most up-to-date deprecation issues.
|
||||
Before upgrading to Elasticsearch 9.0, make sure that you are using the final
|
||||
8.x minor release to see the most up-to-date deprecation issues.
|
||||
|
||||
[float]
|
||||
=== Reindexing
|
||||
|
||||
The *Indices* page lists the indices that are incompatible with the next
|
||||
major version of {es}. You can initiate a reindex to resolve the issues.
|
||||
|
||||
[role="screenshot"]
|
||||
image::images/management-upgrade-assistant-8.0.png[]
|
||||
image::images/management-upgrade-assistant-9.0.png[]
|
||||
|
||||
For a preview of how the data will change during the reindex, select the
|
||||
index name. A warning appears if the index requires destructive changes.
|
||||
Back up your index, then proceed with the reindex by accepting each breaking change.
|
||||
|
||||
You can follow the progress as the Upgrade Assistant makes the index read-only,
|
||||
creates a new index, reindexes the documents, and creates an alias that points
|
||||
from the old index to the new one.
|
||||
|
||||
If the reindexing fails or is cancelled, the changes are rolled back, the
|
||||
new index is deleted, and the original index becomes writable. An error
|
||||
message explains the reason for the failure.
|
||||
|
||||
You can reindex multiple indices at a time, but keep an eye on the
|
||||
{es} metrics, including CPU usage, memory pressure, and disk usage. If a
|
||||
metric is so high it affects query performance, cancel the reindex and
|
||||
continue by reindexing fewer indices at a time.
|
||||
|
||||
Additional considerations:
|
||||
|
||||
* During a reindex of a Watcher (`.watches`) index, the Watcher process
|
||||
pauses and no alerts are triggered.
|
||||
|
||||
* During a reindex of a Machine Learning (`.ml-state`) index, the Machine
|
||||
Learning job pauses and models are not trained or updated.
|
||||
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@elastic/datemath": "5.0.2",
|
||||
"@elastic/eui": "7.1.0",
|
||||
"@elastic/eui": "9.0.2",
|
||||
"@elastic/filesaver": "1.1.2",
|
||||
"@elastic/good": "8.1.1-kibana2",
|
||||
"@elastic/numeral": "2.3.2",
|
||||
|
@ -153,7 +153,7 @@
|
|||
"globby": "^8.0.1",
|
||||
"good-squeeze": "2.1.0",
|
||||
"h2o2": "^8.1.2",
|
||||
"handlebars": "4.0.5",
|
||||
"handlebars": "4.0.13",
|
||||
"hapi": "^17.5.3",
|
||||
"hjson": "3.1.0",
|
||||
"hoek": "^5.0.4",
|
||||
|
|
|
@ -33,7 +33,8 @@ exports.getKibanaPath = function(config, projectRoot) {
|
|||
if (inConfig && config.kibanaPath !== '.') {
|
||||
throw new Error(
|
||||
'The `kibanaPath` option has been removed from `eslint-import-resolver-kibana`. ' +
|
||||
'During development your plugin must live in `../kibana-extra/{pluginName}` ' +
|
||||
'During development your plugin must live in `./plugins/{pluginName}` ' +
|
||||
'inside the Kibana folder or `../kibana-extra/{pluginName}` ' +
|
||||
'relative to the Kibana folder to work with this package.'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ To target the current development version of Kibana just use the default `maste
|
|||
|
||||
```sh
|
||||
node scripts/generate_plugin my_plugin_name
|
||||
# generates a plugin in `../kibana-extra/my_plugin_name`
|
||||
# generates a plugin in `plugins/my_plugin_name`
|
||||
```
|
||||
|
||||
To target 6.3, use the `6.x` branch (until the `6.3` branch is created).
|
||||
|
|
|
@ -42,7 +42,7 @@ exports.run = function run(argv) {
|
|||
dedent(chalk`
|
||||
{dim usage:} node scripts/generate-plugin {bold [name]}
|
||||
|
||||
generate a fresh Kibana plugin in the ../kibana-extra/ directory
|
||||
generate a fresh Kibana plugin in the plugins/ directory
|
||||
`) + '\n'
|
||||
);
|
||||
process.exit(1);
|
||||
|
@ -50,8 +50,8 @@ exports.run = function run(argv) {
|
|||
|
||||
const name = options._[0];
|
||||
const template = resolve(__dirname, './sao_template');
|
||||
const kibanaExtra = resolve(__dirname, '../../../kibana-extra');
|
||||
const targetPath = resolve(kibanaExtra, snakeCase(name));
|
||||
const kibanaPlugins = resolve(__dirname, '../../plugins');
|
||||
const targetPath = resolve(kibanaPlugins, snakeCase(name));
|
||||
|
||||
sao({
|
||||
template: template,
|
||||
|
|
|
@ -102,7 +102,7 @@ module.exports = function({ name }) {
|
|||
cwd: KBN_DIR,
|
||||
stdio: 'inherit',
|
||||
}).then(() => {
|
||||
const dir = relative(process.cwd(), resolve(KBN_DIR, `../kibana-extra`, snakeCase(name)));
|
||||
const dir = relative(process.cwd(), resolve(KBN_DIR, 'plugins', snakeCase(name)));
|
||||
|
||||
log.success(chalk`🎉
|
||||
|
||||
|
|
|
@ -88,15 +88,16 @@ are running inside of.
|
|||
```
|
||||
|
||||
This works because we moved to a strict location of Kibana plugins,
|
||||
`../kibana-extra/{pluginName}` relative to Kibana. This is one of the reasons we
|
||||
wanted to move towards a setup that looks like this:
|
||||
`./plugins/{pluginName}` inside of Kibana, or `../kibana-extra/{pluginName}`
|
||||
relative to Kibana. This is one of the reasons we wanted to move towards a setup
|
||||
that looks like this:
|
||||
|
||||
```
|
||||
elastic
|
||||
├── kibana
|
||||
└── kibana-extra
|
||||
├── kibana-canvas
|
||||
└── x-pack-kibana
|
||||
└── kibana
|
||||
└── plugins
|
||||
├── kibana-canvas
|
||||
└── x-pack-kibana
|
||||
```
|
||||
|
||||
Relying on `link:` style dependencies means we no longer need to `npm publish`
|
||||
|
@ -119,11 +120,12 @@ yarn kbn bootstrap
|
|||
```
|
||||
|
||||
By default, `@kbn/pm` will bootstrap all packages within Kibana, plus all
|
||||
Kibana plugins located in `../kibana-extra`. There are several options for
|
||||
skipping parts of this, e.g. to skip bootstrapping of Kibana plugins:
|
||||
Kibana plugins located in `./plugins` or `../kibana-extra`. There are several
|
||||
options for skipping parts of this, e.g. to skip bootstrapping of Kibana
|
||||
plugins:
|
||||
|
||||
```
|
||||
yarn kbn bootstrap --skip-kibana-extra
|
||||
yarn kbn bootstrap --skip-kibana-plugins
|
||||
```
|
||||
|
||||
Or just skip few selected packages:
|
||||
|
@ -152,7 +154,7 @@ yarn kbn run build
|
|||
```
|
||||
|
||||
And if needed, you can skip packages in the same way as for bootstrapping, e.g.
|
||||
with `--exclude` and `--skip-kibana-extra`:
|
||||
with `--exclude` and `--skip-kibana-plugins`:
|
||||
|
||||
```
|
||||
yarn kbn run build --exclude kibana
|
||||
|
|
5446
packages/kbn-pm/dist/index.js
vendored
5446
packages/kbn-pm/dist/index.js
vendored
File diff suppressed because it is too large
Load diff
|
@ -35,7 +35,7 @@ function help() {
|
|||
usage: kbn <command> [<args>]
|
||||
|
||||
By default commands are run for Kibana itself, all packages in the 'packages/'
|
||||
folder and for all plugins in '../kibana-extra'.
|
||||
folder and for all plugins in './plugins' and '../kibana-extra'.
|
||||
|
||||
Available commands:
|
||||
|
||||
|
@ -43,10 +43,10 @@ function help() {
|
|||
|
||||
Global options:
|
||||
|
||||
-e, --exclude Exclude specified project. Can be specified multiple times to exclude multiple projects, e.g. '-e kibana -e @kbn/pm'.
|
||||
-i, --include Include only specified projects. If left unspecified, it defaults to including all projects.
|
||||
--oss Do not include the x-pack when running command.
|
||||
--skip-kibana-extra Filter all plugins in ../kibana-extra when running command.
|
||||
-e, --exclude Exclude specified project. Can be specified multiple times to exclude multiple projects, e.g. '-e kibana -e @kbn/pm'.
|
||||
-i, --include Include only specified projects. If left unspecified, it defaults to including all projects.
|
||||
--oss Do not include the x-pack when running command.
|
||||
--skip-kibana-plugins Filter all plugins in ./plugins and ../kibana-extra when running command.
|
||||
`);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { resolve } from 'path';
|
||||
|
||||
export interface IProjectPathOptions {
|
||||
'skip-kibana-extra'?: boolean;
|
||||
'skip-kibana-plugins'?: boolean;
|
||||
oss?: boolean;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export interface IProjectPathOptions {
|
|||
* Returns all the paths where plugins are located
|
||||
*/
|
||||
export function getProjectPaths(rootPath: string, options: IProjectPathOptions) {
|
||||
const skipKibanaExtra = Boolean(options['skip-kibana-extra']);
|
||||
const skipKibanaPlugins = Boolean(options['skip-kibana-plugins']);
|
||||
const ossOnly = Boolean(options.oss);
|
||||
|
||||
const projectPaths = [rootPath, resolve(rootPath, 'packages/*')];
|
||||
|
@ -49,10 +49,13 @@ export function getProjectPaths(rootPath: string, options: IProjectPathOptions)
|
|||
projectPaths.push(resolve(rootPath, 'x-pack/plugins/*'));
|
||||
}
|
||||
|
||||
if (!skipKibanaExtra) {
|
||||
if (!skipKibanaPlugins) {
|
||||
projectPaths.push(resolve(rootPath, '../kibana-extra/*'));
|
||||
projectPaths.push(resolve(rootPath, '../kibana-extra/*/packages/*'));
|
||||
projectPaths.push(resolve(rootPath, '../kibana-extra/*/plugins/*'));
|
||||
projectPaths.push(resolve(rootPath, 'plugins/*'));
|
||||
projectPaths.push(resolve(rootPath, 'plugins/*/packages/*'));
|
||||
projectPaths.push(resolve(rootPath, 'plugins/*/plugins/*'));
|
||||
}
|
||||
|
||||
return projectPaths;
|
||||
|
|
|
@ -21,7 +21,8 @@ import { isLinkDependency } from '../utils/package_json';
|
|||
import { Project } from '../utils/project';
|
||||
|
||||
/**
|
||||
* All external projects are located within `../kibana-extra/{plugin}` relative
|
||||
* All external projects are located within `./plugins/{plugin}` relative
|
||||
* to the Kibana root directory or `../kibana-extra/{plugin}` relative
|
||||
* to Kibana itself.
|
||||
*/
|
||||
const isKibanaDep = (depVersion: string) => depVersion.includes('../../kibana/');
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "corge",
|
||||
"version": "1.0.0"
|
||||
}
|
|
@ -17,7 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { mkdir, symlink } from 'fs';
|
||||
import { join, resolve } from 'path';
|
||||
import rmdir from 'rimraf';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import { getProjectPaths } from '../config';
|
||||
import { Project } from './project';
|
||||
|
@ -31,8 +34,20 @@ import {
|
|||
} from './projects';
|
||||
|
||||
const rootPath = resolve(`${__dirname}/__fixtures__/kibana`);
|
||||
const rootPlugins = join(rootPath, 'plugins');
|
||||
|
||||
describe('#getProjects', () => {
|
||||
beforeAll(async () => {
|
||||
await promisify(mkdir)(rootPlugins);
|
||||
|
||||
return promisify(symlink)(
|
||||
join(__dirname, '__fixtures__/symlinked-plugins/corge'),
|
||||
join(rootPlugins, 'corge')
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => promisify(rmdir)(rootPlugins));
|
||||
|
||||
test('find all packages in the packages directory', async () => {
|
||||
const projects = await getProjects(rootPath, ['packages/*']);
|
||||
|
||||
|
@ -68,7 +83,15 @@ describe('#getProjects', () => {
|
|||
const projectPaths = getProjectPaths(rootPath, {});
|
||||
const projects = await getProjects(rootPath, projectPaths);
|
||||
|
||||
const expectedProjects = ['kibana', 'bar', 'foo', 'with-additional-projects', 'quux', 'baz'];
|
||||
const expectedProjects = [
|
||||
'kibana',
|
||||
'bar',
|
||||
'foo',
|
||||
'with-additional-projects',
|
||||
'quux',
|
||||
'baz',
|
||||
'bar',
|
||||
];
|
||||
|
||||
expect([...projects.keys()]).toEqual(expect.arrayContaining(expectedProjects));
|
||||
expect(projects.size).toBe(expectedProjects.length);
|
||||
|
@ -85,7 +108,12 @@ describe('#getProjects', () => {
|
|||
exclude: ['foo', 'bar', 'baz'],
|
||||
});
|
||||
|
||||
expect([...projects.keys()].sort()).toEqual(['kibana', 'quux', 'with-additional-projects']);
|
||||
expect([...projects.keys()].sort()).toEqual([
|
||||
'corge',
|
||||
'kibana',
|
||||
'quux',
|
||||
'with-additional-projects',
|
||||
]);
|
||||
});
|
||||
|
||||
test('ignores unknown projects specified in `exclude` filter', async () => {
|
||||
|
@ -95,6 +123,7 @@ describe('#getProjects', () => {
|
|||
|
||||
expect([...projects.keys()].sort()).toEqual([
|
||||
'baz',
|
||||
'corge',
|
||||
'foo',
|
||||
'kibana',
|
||||
'quux',
|
||||
|
@ -137,7 +166,7 @@ describe('#getProjects', () => {
|
|||
|
||||
test('does not return any project if `exclude` filter is specified for all projects', async () => {
|
||||
const projects = await getProjects(rootPath, projectPaths, {
|
||||
exclude: ['kibana', 'bar', 'foo', 'with-additional-projects', 'quux', 'baz'],
|
||||
exclude: ['kibana', 'bar', 'corge', 'foo', 'with-additional-projects', 'quux', 'baz'],
|
||||
});
|
||||
|
||||
expect(projects.size).toBe(0);
|
||||
|
|
|
@ -54,18 +54,20 @@ export interface LegacyPlatformParams {
|
|||
export class LegacyPlatformService {
|
||||
constructor(private readonly params: LegacyPlatformParams) {}
|
||||
|
||||
public start({
|
||||
i18n,
|
||||
injectedMetadata,
|
||||
fatalErrors,
|
||||
notifications,
|
||||
http,
|
||||
basePath,
|
||||
uiSettings,
|
||||
chrome,
|
||||
}: Deps) {
|
||||
public start(deps: Deps) {
|
||||
const {
|
||||
i18n,
|
||||
injectedMetadata,
|
||||
fatalErrors,
|
||||
notifications,
|
||||
http,
|
||||
basePath,
|
||||
uiSettings,
|
||||
chrome,
|
||||
} = deps;
|
||||
// Inject parts of the new platform into parts of the legacy platform
|
||||
// so that legacy APIs/modules can mimic their new platform counterparts
|
||||
require('ui/new_platform').__newPlatformInit__(deps);
|
||||
require('ui/metadata').__newPlatformInit__(injectedMetadata.getLegacyMetadata());
|
||||
require('ui/i18n').__newPlatformInit__(i18n.Context);
|
||||
require('ui/notify/fatal_error').__newPlatformInit__(fatalErrors);
|
||||
|
|
50
src/core/server/config/config_service.mock.ts
Normal file
50
src/core/server/config/config_service.mock.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 { BehaviorSubject } from 'rxjs';
|
||||
import { ObjectToConfigAdapter } from './object_to_config_adapter';
|
||||
|
||||
import { ConfigService } from './config_service';
|
||||
|
||||
type MethodKeysOf<T> = {
|
||||
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never
|
||||
}[keyof T];
|
||||
|
||||
type PublicMethodsOf<T> = Pick<T, MethodKeysOf<T>>;
|
||||
|
||||
type ConfigSericeContract = PublicMethodsOf<ConfigService>;
|
||||
const createConfigServiceMock = () => {
|
||||
const mocked: jest.Mocked<ConfigSericeContract> = {
|
||||
atPath: jest.fn(),
|
||||
getConfig$: jest.fn(),
|
||||
optionalAtPath: jest.fn(),
|
||||
getUsedPaths: jest.fn(),
|
||||
getUnusedPaths: jest.fn(),
|
||||
isEnabledAtPath: jest.fn(),
|
||||
};
|
||||
mocked.atPath.mockReturnValue(new BehaviorSubject({}));
|
||||
mocked.getConfig$.mockReturnValue(new BehaviorSubject(new ObjectToConfigAdapter({})));
|
||||
mocked.getUsedPaths.mockResolvedValue([]);
|
||||
mocked.getUnusedPaths.mockResolvedValue([]);
|
||||
mocked.isEnabledAtPath.mockResolvedValue(true);
|
||||
return mocked;
|
||||
};
|
||||
|
||||
export const configServiceMock = {
|
||||
create: createConfigServiceMock,
|
||||
};
|
|
@ -23,16 +23,17 @@ import { BehaviorSubject } from 'rxjs';
|
|||
import { first } from 'rxjs/operators';
|
||||
|
||||
const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
|
||||
jest.mock('../../../legacy/utils/package_json', () => ({ pkg: mockPackage }));
|
||||
jest.mock('../../../../package.json', () => mockPackage);
|
||||
|
||||
import { schema, Type, TypeOf } from '@kbn/config-schema';
|
||||
|
||||
import { ConfigService, Env, ObjectToConfigAdapter } from '.';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { getEnvOptions } from './__mocks__/env';
|
||||
|
||||
const emptyArgv = getEnvOptions();
|
||||
const defaultEnv = new Env('/kibana', emptyArgv);
|
||||
const logger = loggingServiceMock.create();
|
||||
|
||||
test('returns config at path as observable', async () => {
|
||||
const config$ = new BehaviorSubject(new ObjectToConfigAdapter({ key: 'foo' }));
|
||||
|
|
|
@ -30,7 +30,7 @@ jest.mock('path', () => ({
|
|||
}));
|
||||
|
||||
const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
|
||||
jest.mock('../../../legacy/utils/package_json', () => ({ pkg: mockPackage }));
|
||||
jest.mock('../../../../package.json', () => mockPackage);
|
||||
|
||||
import { Env } from '.';
|
||||
import { getEnvOptions } from './__mocks__/env';
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
import { resolve } from 'path';
|
||||
import process from 'process';
|
||||
|
||||
import { pkg } from '../../../legacy/utils/package_json';
|
||||
// `require` is necessary for this to work inside x-pack code as well
|
||||
// tslint:disable no-var-requires
|
||||
const pkg = require('../../../../package.json');
|
||||
|
||||
export interface PackageInfo {
|
||||
version: string;
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ConfigService, Env } from '../server/config';
|
||||
import { LoggerFactory } from '../server/logging';
|
||||
import { ConfigService, Env } from './config';
|
||||
import { LoggerFactory } from './logging';
|
||||
|
||||
/**
|
||||
* Groups all main Kibana's core modules/systems/services that are consumed in a
|
|
@ -39,9 +39,10 @@ jest.mock('./elasticsearch_client_config', () => ({
|
|||
import { errors } from 'elasticsearch';
|
||||
import { get } from 'lodash';
|
||||
import { Logger } from '../logging';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { ClusterClient } from './cluster_client';
|
||||
|
||||
const logger = loggingServiceMock.create();
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
test('#constructor creates client with parsed config', () => {
|
||||
|
|
|
@ -21,12 +21,12 @@ const mockReadFileSync = jest.fn();
|
|||
jest.mock('fs', () => ({ readFileSync: mockReadFileSync }));
|
||||
|
||||
import { duration } from 'moment';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import {
|
||||
ElasticsearchClientConfig,
|
||||
parseElasticsearchClientConfig,
|
||||
} from './elasticsearch_client_config';
|
||||
|
||||
const logger = loggingServiceMock.create();
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
test('parses minimally specified config', () => {
|
||||
|
@ -365,7 +365,7 @@ describe('#log', () => {
|
|||
|
||||
expect(typeof esLogger.close).toBe('function');
|
||||
|
||||
expect(logger.mockCollect()).toMatchInlineSnapshot(`
|
||||
expect(loggingServiceMock.collect(logger)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"debug": Array [],
|
||||
"error": Array [
|
||||
|
@ -411,7 +411,7 @@ Object {
|
|||
|
||||
expect(typeof esLogger.close).toBe('function');
|
||||
|
||||
expect(logger.mockCollect()).toMatchInlineSnapshot(`
|
||||
expect(loggingServiceMock.collect(logger)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"debug": Array [
|
||||
Array [
|
||||
|
|
57
src/core/server/elasticsearch/elasticsearch_service.mock.ts
Normal file
57
src/core/server/elasticsearch/elasticsearch_service.mock.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { BehaviorSubject } from 'rxjs';
|
||||
import { ClusterClient } from './cluster_client';
|
||||
import { ElasticsearchConfig } from './elasticsearch_config';
|
||||
import { ElasticsearchService, ElasticsearchServiceStart } from './elasticsearch_service';
|
||||
|
||||
const createStartContractMock = () => {
|
||||
const startContract: ElasticsearchServiceStart = {
|
||||
legacy: {
|
||||
config$: new BehaviorSubject({} as ElasticsearchConfig),
|
||||
},
|
||||
|
||||
createClient: jest.fn(),
|
||||
adminClient$: new BehaviorSubject({} as ClusterClient),
|
||||
dataClient$: new BehaviorSubject({} as ClusterClient),
|
||||
};
|
||||
return startContract;
|
||||
};
|
||||
|
||||
type MethodKeysOf<T> = {
|
||||
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never
|
||||
}[keyof T];
|
||||
|
||||
type PublicMethodsOf<T> = Pick<T, MethodKeysOf<T>>;
|
||||
|
||||
type ElasticsearchServiceContract = PublicMethodsOf<ElasticsearchService>;
|
||||
const createMock = () => {
|
||||
const mocked: jest.Mocked<ElasticsearchServiceContract> = {
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
};
|
||||
mocked.start.mockResolvedValue(createStartContractMock());
|
||||
mocked.stop.mockResolvedValue();
|
||||
return mocked;
|
||||
};
|
||||
|
||||
export const elasticsearchServiceMock = {
|
||||
create: createMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
|
@ -23,10 +23,10 @@ const MockClusterClient = jest.fn();
|
|||
jest.mock('./cluster_client', () => ({ ClusterClient: MockClusterClient }));
|
||||
|
||||
import { BehaviorSubject, combineLatest } from 'rxjs';
|
||||
import { CoreContext } from '../../types';
|
||||
import { Config, ConfigService, Env, ObjectToConfigAdapter } from '../config';
|
||||
import { getEnvOptions } from '../config/__mocks__/env';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { ElasticsearchConfig } from './elasticsearch_config';
|
||||
import { ElasticsearchService } from './elasticsearch_service';
|
||||
|
||||
|
@ -34,6 +34,7 @@ let elasticsearchService: ElasticsearchService;
|
|||
let configService: ConfigService;
|
||||
let env: Env;
|
||||
let coreContext: CoreContext;
|
||||
const logger = loggingServiceMock.create();
|
||||
beforeEach(() => {
|
||||
env = Env.createDefault(getEnvOptions());
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
import { ConnectableObservable, Observable, Subscription } from 'rxjs';
|
||||
import { filter, map, publishReplay, switchMap } from 'rxjs/operators';
|
||||
import { CoreContext, CoreService } from '../../types';
|
||||
import { CoreService } from '../../types';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { Logger } from '../logging';
|
||||
import { ClusterClient } from './cluster_client';
|
||||
import { ElasticsearchClientConfig } from './elasticsearch_client_config';
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
export { ElasticsearchServiceStart } from './elasticsearch_service';
|
||||
export { CallAPIOptions, ClusterClient } from './cluster_client';
|
||||
|
||||
import { CoreContext } from '../../types';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { ElasticsearchService } from './elasticsearch_service';
|
||||
|
||||
/** @internal */
|
||||
|
|
|
@ -28,7 +28,7 @@ import supertest from 'supertest';
|
|||
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { HttpConfig, Router } from '.';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { HttpServer } from './http_server';
|
||||
|
||||
const chance = new Chance();
|
||||
|
@ -36,6 +36,7 @@ const chance = new Chance();
|
|||
let server: HttpServer;
|
||||
let config: HttpConfig;
|
||||
|
||||
const logger = loggingServiceMock.create();
|
||||
beforeEach(() => {
|
||||
config = {
|
||||
host: '127.0.0.1',
|
||||
|
@ -49,7 +50,7 @@ beforeEach(() => {
|
|||
|
||||
afterEach(async () => {
|
||||
await server.stop();
|
||||
logger.mockClear();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('listening after started', async () => {
|
||||
|
|
51
src/core/server/http/http_service.mock.ts
Normal file
51
src/core/server/http/http_service.mock.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { Server, ServerOptions } from 'hapi';
|
||||
import { HttpService } from './http_service';
|
||||
|
||||
const createStartContractMock = () => {
|
||||
const startContract = {
|
||||
// we can mock some hapi server method when we need it
|
||||
server: {} as Server,
|
||||
options: {} as ServerOptions,
|
||||
};
|
||||
return startContract;
|
||||
};
|
||||
|
||||
type MethodKeysOf<T> = {
|
||||
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never
|
||||
}[keyof T];
|
||||
|
||||
type PublicMethodsOf<T> = Pick<T, MethodKeysOf<T>>;
|
||||
|
||||
type HttpSericeContract = PublicMethodsOf<HttpService>;
|
||||
const createHttpServiceMock = () => {
|
||||
const mocked: jest.Mocked<HttpSericeContract> = {
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
registerRouter: jest.fn(),
|
||||
};
|
||||
mocked.start.mockResolvedValue(createStartContractMock());
|
||||
return mocked;
|
||||
};
|
||||
|
||||
export const httpServiceMock = {
|
||||
create: createHttpServiceMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
|
@ -26,11 +26,12 @@ jest.mock('./http_server', () => ({
|
|||
import { noop } from 'lodash';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { HttpConfig, HttpService, Router } from '.';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
|
||||
beforeEach(() => {
|
||||
logger.mockClear();
|
||||
mockHttpServer.mockClear();
|
||||
const logger = loggingServiceMock.create();
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('creates and starts http server', async () => {
|
||||
|
@ -75,7 +76,7 @@ test('logs error if already started', async () => {
|
|||
|
||||
await service.start();
|
||||
|
||||
expect(logger.mockCollect()).toMatchSnapshot();
|
||||
expect(loggingServiceMock.collect(logger)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('stops http server', async () => {
|
||||
|
@ -121,7 +122,7 @@ test('register route handler', () => {
|
|||
|
||||
expect(httpServer.registerRouter).toHaveBeenCalledTimes(1);
|
||||
expect(httpServer.registerRouter).toHaveBeenLastCalledWith(router);
|
||||
expect(logger.mockCollect()).toMatchSnapshot();
|
||||
expect(loggingServiceMock.collect(logger)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws if registering route handler after http server is started', () => {
|
||||
|
@ -143,7 +144,7 @@ test('throws if registering route handler after http server is started', () => {
|
|||
service.registerRouter(router);
|
||||
|
||||
expect(httpServer.registerRouter).toHaveBeenCalledTimes(0);
|
||||
expect(logger.mockCollect()).toMatchSnapshot();
|
||||
expect(loggingServiceMock.collect(logger)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('returns http server contract on start', async () => {
|
||||
|
|
|
@ -27,7 +27,7 @@ import supertest from 'supertest';
|
|||
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { HttpConfig } from '.';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { HttpsRedirectServer } from './https_redirect_server';
|
||||
|
||||
const chance = new Chance();
|
||||
|
@ -50,12 +50,11 @@ beforeEach(() => {
|
|||
},
|
||||
} as HttpConfig;
|
||||
|
||||
server = new HttpsRedirectServer(logger.get());
|
||||
server = new HttpsRedirectServer(loggingServiceMock.create().get());
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await server.stop();
|
||||
logger.mockClear();
|
||||
});
|
||||
|
||||
test('throws if SSL is not enabled', async () => {
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
const mockHttpService = { start: jest.fn(), stop: jest.fn(), registerRouter: jest.fn() };
|
||||
import { httpServiceMock } from './http/http_service.mock';
|
||||
const httpService = httpServiceMock.create();
|
||||
jest.mock('./http/http_service', () => ({
|
||||
HttpService: jest.fn(() => mockHttpService),
|
||||
HttpService: jest.fn(() => httpService),
|
||||
}));
|
||||
|
||||
const mockPluginsService = { start: jest.fn(), stop: jest.fn() };
|
||||
|
@ -27,9 +27,10 @@ jest.mock('./plugins/plugins_service', () => ({
|
|||
PluginsService: jest.fn(() => mockPluginsService),
|
||||
}));
|
||||
|
||||
const mockElasticsearchService = { start: jest.fn(), stop: jest.fn() };
|
||||
import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
|
||||
const elasticsearchService = elasticsearchServiceMock.create();
|
||||
jest.mock('./elasticsearch/elasticsearch_service', () => ({
|
||||
ElasticsearchService: jest.fn(() => mockElasticsearchService),
|
||||
ElasticsearchService: jest.fn(() => elasticsearchService),
|
||||
}));
|
||||
|
||||
const mockLegacyService = { start: jest.fn(), stop: jest.fn() };
|
||||
|
@ -41,22 +42,26 @@ import { BehaviorSubject } from 'rxjs';
|
|||
import { Server } from '.';
|
||||
import { Env } from './config';
|
||||
import { getEnvOptions } from './config/__mocks__/env';
|
||||
import { logger } from './logging/__mocks__';
|
||||
import { loggingServiceMock } from './logging/logging_service.mock';
|
||||
|
||||
const mockConfigService = { atPath: jest.fn(), getUnusedPaths: jest.fn().mockReturnValue([]) };
|
||||
import { configServiceMock } from './config/config_service.mock';
|
||||
|
||||
const configService = configServiceMock.create();
|
||||
const env = new Env('.', getEnvOptions());
|
||||
const logger = loggingServiceMock.create();
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfigService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
|
||||
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
logger.mockClear();
|
||||
mockConfigService.atPath.mockReset();
|
||||
mockHttpService.start.mockReset();
|
||||
mockHttpService.stop.mockReset();
|
||||
mockElasticsearchService.start.mockReset();
|
||||
mockElasticsearchService.stop.mockReset();
|
||||
jest.clearAllMocks();
|
||||
|
||||
configService.atPath.mockReset();
|
||||
httpService.start.mockReset();
|
||||
httpService.stop.mockReset();
|
||||
elasticsearchService.start.mockReset();
|
||||
elasticsearchService.stop.mockReset();
|
||||
mockPluginsService.start.mockReset();
|
||||
mockPluginsService.stop.mockReset();
|
||||
mockLegacyService.start.mockReset();
|
||||
|
@ -64,65 +69,49 @@ afterEach(() => {
|
|||
});
|
||||
|
||||
test('starts services on "start"', async () => {
|
||||
const mockHttpServiceStart = { something: true };
|
||||
mockHttpService.start.mockReturnValue(Promise.resolve(mockHttpServiceStart));
|
||||
|
||||
const mockElasticsearchServiceStart = { adminClient$: {} };
|
||||
mockElasticsearchService.start.mockResolvedValue(mockElasticsearchServiceStart);
|
||||
|
||||
const mockPluginsServiceStart = new Map([['some-plugin', 'some-value']]);
|
||||
mockPluginsService.start.mockReturnValue(Promise.resolve(mockPluginsServiceStart));
|
||||
|
||||
const server = new Server(mockConfigService as any, logger, env);
|
||||
const server = new Server(configService as any, logger, env);
|
||||
|
||||
expect(mockHttpService.start).not.toHaveBeenCalled();
|
||||
expect(mockElasticsearchService.start).not.toHaveBeenCalled();
|
||||
expect(httpService.start).not.toHaveBeenCalled();
|
||||
expect(elasticsearchService.start).not.toHaveBeenCalled();
|
||||
expect(mockPluginsService.start).not.toHaveBeenCalled();
|
||||
expect(mockLegacyService.start).not.toHaveBeenCalled();
|
||||
|
||||
await server.start();
|
||||
|
||||
expect(mockHttpService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockElasticsearchService.start).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(httpService.start).toHaveBeenCalledTimes(1);
|
||||
expect(elasticsearchService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockPluginsService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockPluginsService.start).toHaveBeenCalledWith({
|
||||
elasticsearch: mockElasticsearchServiceStart,
|
||||
});
|
||||
|
||||
expect(mockLegacyService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockLegacyService.start).toHaveBeenCalledWith({
|
||||
elasticsearch: mockElasticsearchServiceStart,
|
||||
http: mockHttpServiceStart,
|
||||
plugins: mockPluginsServiceStart,
|
||||
});
|
||||
});
|
||||
|
||||
test('does not fail on "start" if there are unused paths detected', async () => {
|
||||
mockConfigService.getUnusedPaths.mockReturnValue(['some.path', 'another.path']);
|
||||
configService.getUnusedPaths.mockResolvedValue(['some.path', 'another.path']);
|
||||
|
||||
const server = new Server(mockConfigService as any, logger, env);
|
||||
const server = new Server(configService as any, logger, env);
|
||||
await expect(server.start()).resolves.toBeUndefined();
|
||||
expect(logger.mockCollect()).toMatchSnapshot('unused paths logs');
|
||||
expect(loggingServiceMock.collect(logger)).toMatchSnapshot('unused paths logs');
|
||||
});
|
||||
|
||||
test('does not start http service is `autoListen:false`', async () => {
|
||||
mockConfigService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: false }));
|
||||
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: false }));
|
||||
|
||||
const server = new Server(mockConfigService as any, logger, env);
|
||||
const server = new Server(configService as any, logger, env);
|
||||
|
||||
expect(mockLegacyService.start).not.toHaveBeenCalled();
|
||||
|
||||
await server.start();
|
||||
|
||||
expect(mockHttpService.start).not.toHaveBeenCalled();
|
||||
expect(httpService.start).not.toHaveBeenCalled();
|
||||
expect(mockLegacyService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockLegacyService.start).toHaveBeenCalledWith({});
|
||||
});
|
||||
|
||||
test('does not start http service if process is dev cluster master', async () => {
|
||||
const server = new Server(
|
||||
mockConfigService as any,
|
||||
configService as any,
|
||||
logger,
|
||||
new Env('.', getEnvOptions({ isDevClusterMaster: true }))
|
||||
);
|
||||
|
@ -131,28 +120,25 @@ test('does not start http service if process is dev cluster master', async () =>
|
|||
|
||||
await server.start();
|
||||
|
||||
expect(mockHttpService.start).not.toHaveBeenCalled();
|
||||
expect(httpService.start).not.toHaveBeenCalled();
|
||||
expect(mockLegacyService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockLegacyService.start).toHaveBeenCalledWith({});
|
||||
});
|
||||
|
||||
test('stops services on "stop"', async () => {
|
||||
const mockHttpServiceStart = { something: true };
|
||||
mockHttpService.start.mockReturnValue(Promise.resolve(mockHttpServiceStart));
|
||||
|
||||
const server = new Server(mockConfigService as any, logger, env);
|
||||
const server = new Server(configService as any, logger, env);
|
||||
|
||||
await server.start();
|
||||
|
||||
expect(mockHttpService.stop).not.toHaveBeenCalled();
|
||||
expect(mockElasticsearchService.stop).not.toHaveBeenCalled();
|
||||
expect(httpService.stop).not.toHaveBeenCalled();
|
||||
expect(elasticsearchService.stop).not.toHaveBeenCalled();
|
||||
expect(mockPluginsService.stop).not.toHaveBeenCalled();
|
||||
expect(mockLegacyService.stop).not.toHaveBeenCalled();
|
||||
|
||||
await server.stop();
|
||||
|
||||
expect(mockHttpService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockElasticsearchService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(httpService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(elasticsearchService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockPluginsService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockLegacyService.stop).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreContext } from '../../types';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { LegacyService } from './legacy_service';
|
||||
|
||||
/** @internal */
|
||||
|
|
|
@ -28,10 +28,11 @@ import { LegacyService } from '.';
|
|||
// @ts-ignore: implicit any for JS file
|
||||
import MockClusterManager from '../../../cli/cluster/cluster_manager';
|
||||
import KbnServer from '../../../legacy/server/kbn_server';
|
||||
import { Config, ConfigService, Env, ObjectToConfigAdapter } from '../config';
|
||||
import { Config, Env, ObjectToConfigAdapter } from '../config';
|
||||
import { getEnvOptions } from '../config/__mocks__/env';
|
||||
import { configServiceMock } from '../config/config_service.mock';
|
||||
import { ElasticsearchServiceStart } from '../elasticsearch';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { PluginsServiceStart } from '../plugins/plugins_service';
|
||||
import { LegacyPlatformProxy } from './legacy_platform_proxy';
|
||||
|
||||
|
@ -39,7 +40,6 @@ const MockKbnServer: jest.Mock<KbnServer> = KbnServer as any;
|
|||
const MockLegacyPlatformProxy: jest.Mock<LegacyPlatformProxy> = LegacyPlatformProxy as any;
|
||||
|
||||
let legacyService: LegacyService;
|
||||
let configService: jest.Mocked<ConfigService>;
|
||||
let env: Env;
|
||||
let config$: BehaviorSubject<Config>;
|
||||
let startDeps: {
|
||||
|
@ -47,8 +47,12 @@ let startDeps: {
|
|||
http: any;
|
||||
plugins: PluginsServiceStart;
|
||||
};
|
||||
const logger = loggingServiceMock.create();
|
||||
let configService: ReturnType<typeof configServiceMock.create>;
|
||||
|
||||
beforeEach(() => {
|
||||
env = Env.createDefault(getEnvOptions());
|
||||
configService = configServiceMock.create();
|
||||
|
||||
MockKbnServer.prototype.ready = jest.fn().mockReturnValue(Promise.resolve());
|
||||
|
||||
|
@ -68,19 +72,14 @@ beforeEach(() => {
|
|||
})
|
||||
);
|
||||
|
||||
configService = {
|
||||
getConfig$: jest.fn().mockReturnValue(config$),
|
||||
atPath: jest.fn().mockReturnValue(new BehaviorSubject({})),
|
||||
getUsedPaths: jest.fn().mockReturnValue(['foo.bar']),
|
||||
} as any;
|
||||
legacyService = new LegacyService({ env, logger, configService });
|
||||
configService.getConfig$.mockReturnValue(config$);
|
||||
configService.getUsedPaths.mockResolvedValue(['foo.bar']);
|
||||
|
||||
legacyService = new LegacyService({ env, logger, configService: configService as any });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
MockLegacyPlatformProxy.mockClear();
|
||||
MockKbnServer.mockClear();
|
||||
MockClusterManager.create.mockClear();
|
||||
logger.mockClear();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('once LegacyService is started with connection info', () => {
|
||||
|
@ -235,7 +234,7 @@ describe('once LegacyService is started with connection info', () => {
|
|||
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances as Array<jest.Mocked<KbnServer>>;
|
||||
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
|
||||
expect(logger.mockCollect().error).toEqual([]);
|
||||
expect(loggingServiceMock.collect(logger).error).toEqual([]);
|
||||
|
||||
const configError = new Error('something went wrong');
|
||||
mockKbnServer.applyLoggingConfiguration.mockImplementation(() => {
|
||||
|
@ -244,7 +243,7 @@ describe('once LegacyService is started with connection info', () => {
|
|||
|
||||
config$.next(new ObjectToConfigAdapter({ logging: { verbose: true } }));
|
||||
|
||||
expect(logger.mockCollect().error).toEqual([[configError]]);
|
||||
expect(loggingServiceMock.collect(logger).error).toEqual([[configError]]);
|
||||
});
|
||||
|
||||
test('logs error if config service fails.', async () => {
|
||||
|
@ -252,13 +251,13 @@ describe('once LegacyService is started with connection info', () => {
|
|||
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances;
|
||||
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
|
||||
expect(logger.mockCollect().error).toEqual([]);
|
||||
expect(loggingServiceMock.collect(logger).error).toEqual([]);
|
||||
|
||||
const configError = new Error('something went wrong');
|
||||
config$.error(configError);
|
||||
|
||||
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
|
||||
expect(logger.mockCollect().error).toEqual([[configError]]);
|
||||
expect(loggingServiceMock.collect(logger).error).toEqual([[configError]]);
|
||||
});
|
||||
|
||||
test('proxy route abandons request processing and forwards it to the legacy Kibana', async () => {
|
||||
|
@ -337,7 +336,7 @@ describe('once LegacyService is started in `devClusterMaster` mode', () => {
|
|||
})
|
||||
),
|
||||
logger,
|
||||
configService,
|
||||
configService: configService as any,
|
||||
});
|
||||
|
||||
await devClusterLegacyService.start({
|
||||
|
@ -359,7 +358,7 @@ describe('once LegacyService is started in `devClusterMaster` mode', () => {
|
|||
})
|
||||
),
|
||||
logger,
|
||||
configService,
|
||||
configService: configService as any,
|
||||
});
|
||||
|
||||
await devClusterLegacyService.start({
|
||||
|
|
|
@ -20,8 +20,9 @@
|
|||
import { Server as HapiServer } from 'hapi';
|
||||
import { combineLatest, ConnectableObservable, EMPTY, Subscription } from 'rxjs';
|
||||
import { first, map, mergeMap, publishReplay, tap } from 'rxjs/operators';
|
||||
import { CoreContext, CoreService } from '../../types';
|
||||
import { CoreService } from '../../types';
|
||||
import { Config } from '../config';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { DevConfig } from '../dev';
|
||||
import { ElasticsearchServiceStart } from '../elasticsearch';
|
||||
import { BasePathProxyServer, HttpConfig, HttpServiceStart } from '../http';
|
||||
|
|
|
@ -1,61 +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.
|
||||
*/
|
||||
|
||||
// Test helpers to simplify mocking logs and collecting all their outputs
|
||||
|
||||
const mockLog = {
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
fatal: jest.fn(),
|
||||
info: jest.fn(),
|
||||
log: jest.fn(),
|
||||
trace: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
};
|
||||
|
||||
const mockClear = () => {
|
||||
logger.get.mockClear();
|
||||
mockLog.debug.mockClear();
|
||||
mockLog.info.mockClear();
|
||||
mockLog.warn.mockClear();
|
||||
mockLog.error.mockClear();
|
||||
mockLog.trace.mockClear();
|
||||
mockLog.fatal.mockClear();
|
||||
mockLog.log.mockClear();
|
||||
};
|
||||
|
||||
const mockCollect = () => ({
|
||||
debug: mockLog.debug.mock.calls,
|
||||
error: mockLog.error.mock.calls,
|
||||
fatal: mockLog.fatal.mock.calls,
|
||||
info: mockLog.info.mock.calls,
|
||||
log: mockLog.log.mock.calls,
|
||||
trace: mockLog.trace.mock.calls,
|
||||
warn: mockLog.warn.mock.calls,
|
||||
});
|
||||
|
||||
export const logger = {
|
||||
get: jest.fn((...context) => ({
|
||||
context,
|
||||
...mockLog,
|
||||
})),
|
||||
mockClear,
|
||||
mockCollect,
|
||||
mockLog,
|
||||
};
|
92
src/core/server/logging/logging_service.mock.ts
Normal file
92
src/core/server/logging/logging_service.mock.ts
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Test helpers to simplify mocking logs and collecting all their outputs
|
||||
import { Logger } from './logger';
|
||||
import { LoggingService } from './logging_service';
|
||||
|
||||
type MethodKeysOf<T> = {
|
||||
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never
|
||||
}[keyof T];
|
||||
|
||||
type PublicMethodsOf<T> = Pick<T, MethodKeysOf<T>>;
|
||||
|
||||
type LoggingServiceContract = PublicMethodsOf<LoggingService>;
|
||||
type MockedLogger = jest.Mocked<Logger>;
|
||||
|
||||
const createLoggingServiceMock = () => {
|
||||
const mockLog: MockedLogger = {
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
fatal: jest.fn(),
|
||||
info: jest.fn(),
|
||||
log: jest.fn(),
|
||||
trace: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
};
|
||||
|
||||
const mocked: jest.Mocked<LoggingServiceContract> = {
|
||||
get: jest.fn(),
|
||||
asLoggerFactory: jest.fn(),
|
||||
upgrade: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
};
|
||||
mocked.get.mockImplementation((...context) => ({
|
||||
context,
|
||||
...mockLog,
|
||||
}));
|
||||
mocked.asLoggerFactory.mockImplementation(() => createLoggingServiceMock());
|
||||
mocked.stop.mockResolvedValue();
|
||||
return mocked;
|
||||
};
|
||||
|
||||
const collectLoggingServiceMock = (mocked: ReturnType<typeof createLoggingServiceMock>) => {
|
||||
const mockLog = mocked.get() as MockedLogger;
|
||||
return {
|
||||
debug: mockLog.debug.mock.calls,
|
||||
error: mockLog.error.mock.calls,
|
||||
fatal: mockLog.fatal.mock.calls,
|
||||
info: mockLog.info.mock.calls,
|
||||
log: mockLog.log.mock.calls,
|
||||
trace: mockLog.trace.mock.calls,
|
||||
warn: mockLog.warn.mock.calls,
|
||||
};
|
||||
};
|
||||
|
||||
const clearLoggingServiceMock = (mocked: ReturnType<typeof createLoggingServiceMock>) => {
|
||||
const mockLog = mocked.get() as MockedLogger;
|
||||
mocked.get.mockClear();
|
||||
mocked.asLoggerFactory.mockClear();
|
||||
mocked.upgrade.mockClear();
|
||||
mocked.stop.mockClear();
|
||||
|
||||
mockLog.debug.mockClear();
|
||||
mockLog.info.mockClear();
|
||||
mockLog.warn.mockClear();
|
||||
mockLog.error.mockClear();
|
||||
mockLog.trace.mockClear();
|
||||
mockLog.fatal.mockClear();
|
||||
mockLog.log.mockClear();
|
||||
};
|
||||
|
||||
export const loggingServiceMock = {
|
||||
create: createLoggingServiceMock,
|
||||
collect: collectLoggingServiceMock,
|
||||
clear: clearLoggingServiceMock,
|
||||
};
|
|
@ -27,14 +27,14 @@ jest.mock('fs', () => ({
|
|||
}));
|
||||
|
||||
const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
|
||||
jest.mock('../../../../legacy/utils/package_json', () => ({ pkg: mockPackage }));
|
||||
jest.mock('../../../../../package.json', () => mockPackage);
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { first, map, toArray } from 'rxjs/operators';
|
||||
import { Config, ConfigService, Env, ObjectToConfigAdapter } from '../../config';
|
||||
import { getEnvOptions } from '../../config/__mocks__/env';
|
||||
import { logger } from '../../logging/__mocks__';
|
||||
import { loggingServiceMock } from '../../logging/logging_service.mock';
|
||||
import { Plugin } from '../plugin';
|
||||
import { PluginsConfig } from '../plugins_config';
|
||||
import { discover } from './plugins_discovery';
|
||||
|
@ -45,6 +45,7 @@ const TEST_PLUGIN_SEARCH_PATHS = {
|
|||
nonExistentKibanaExtra: resolve(process.cwd(), '..', 'kibana-extra'),
|
||||
};
|
||||
|
||||
const logger = loggingServiceMock.create();
|
||||
beforeEach(() => {
|
||||
mockReaddir.mockImplementation((path, cb) => {
|
||||
if (path === TEST_PLUGIN_SEARCH_PATHS.nonEmptySrcPlugins) {
|
||||
|
|
|
@ -21,7 +21,7 @@ import { readdir, stat } from 'fs';
|
|||
import { resolve } from 'path';
|
||||
import { bindNodeCallback, from } from 'rxjs';
|
||||
import { catchError, filter, map, mergeMap, shareReplay } from 'rxjs/operators';
|
||||
import { CoreContext } from '../../../types';
|
||||
import { CoreContext } from '../../core_context';
|
||||
import { Logger } from '../../logging';
|
||||
import { Plugin } from '../plugin';
|
||||
import { createPluginInitializerContext } from '../plugin_context';
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreContext } from '../../types';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { PluginsService } from './plugins_service';
|
||||
|
||||
/** @internal */
|
||||
|
|
|
@ -19,15 +19,16 @@
|
|||
|
||||
import { join } from 'path';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { CoreContext } from '../../types';
|
||||
import { Config, ConfigService, Env, ObjectToConfigAdapter } from '../config';
|
||||
import { getEnvOptions } from '../config/__mocks__/env';
|
||||
import { ElasticsearchServiceStart } from '../elasticsearch';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { Plugin, PluginManifest } from './plugin';
|
||||
import { createPluginInitializerContext, createPluginStartContext } from './plugin_context';
|
||||
|
||||
const mockPluginInitializer = jest.fn();
|
||||
const logger = loggingServiceMock.create();
|
||||
jest.mock(
|
||||
join('plugin-with-initializer-path', 'server'),
|
||||
() => ({ plugin: mockPluginInitializer }),
|
||||
|
@ -57,10 +58,9 @@ function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): Plug
|
|||
let configService: ConfigService;
|
||||
let env: Env;
|
||||
let coreContext: CoreContext;
|
||||
let startDeps: { elasticsearch: ElasticsearchServiceStart };
|
||||
const startDeps = { elasticsearch: elasticsearchServiceMock.createStartContract() };
|
||||
beforeEach(() => {
|
||||
env = Env.createDefault(getEnvOptions());
|
||||
startDeps = { elasticsearch: { adminClient$: {}, dataClient$: {} } as any };
|
||||
|
||||
configService = new ConfigService(
|
||||
new BehaviorSubject<Config>(new ObjectToConfigAdapter({ plugins: { initialize: true } })),
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
import { Type } from '@kbn/config-schema';
|
||||
import { Observable } from 'rxjs';
|
||||
import { CoreContext } from '../../types';
|
||||
import { ConfigWithSchema, EnvironmentMode } from '../config';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { ClusterClient } from '../elasticsearch';
|
||||
import { LoggerFactory } from '../logging';
|
||||
import { Plugin, PluginManifest } from './plugin';
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ElasticsearchServiceStart } from '../elasticsearch';
|
||||
|
||||
const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
|
||||
jest.mock('../../../legacy/utils/package_json', () => ({ pkg: mockPackage }));
|
||||
|
||||
|
@ -32,7 +30,8 @@ import { BehaviorSubject, from } from 'rxjs';
|
|||
|
||||
import { Config, ConfigService, Env, ObjectToConfigAdapter } from '../config';
|
||||
import { getEnvOptions } from '../config/__mocks__/env';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { PluginDiscoveryError } from './discovery';
|
||||
import { Plugin } from './plugin';
|
||||
import { PluginsService } from './plugins_service';
|
||||
|
@ -44,7 +43,8 @@ let pluginsService: PluginsService;
|
|||
let configService: ConfigService;
|
||||
let env: Env;
|
||||
let mockPluginSystem: jest.Mocked<PluginsSystem>;
|
||||
let startDeps: { elasticsearch: ElasticsearchServiceStart };
|
||||
const startDeps = { elasticsearch: elasticsearchServiceMock.createStartContract() };
|
||||
const logger = loggingServiceMock.create();
|
||||
beforeEach(() => {
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
|
@ -57,7 +57,6 @@ beforeEach(() => {
|
|||
};
|
||||
|
||||
env = Env.createDefault(getEnvOptions());
|
||||
startDeps = { elasticsearch: { legacy: {} } as any };
|
||||
|
||||
configService = new ConfigService(
|
||||
new BehaviorSubject<Config>(new ObjectToConfigAdapter({ plugins: { initialize: true } })),
|
||||
|
@ -83,7 +82,7 @@ test('`start` throws if plugin has an invalid manifest', async () => {
|
|||
[Error: Failed to initialize plugins:
|
||||
Invalid JSON (invalid-manifest, path-1)]
|
||||
`);
|
||||
expect(logger.mockCollect().error).toMatchInlineSnapshot(`
|
||||
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
[Error: Invalid JSON (invalid-manifest, path-1)],
|
||||
|
@ -104,7 +103,7 @@ test('`start` throws if plugin required Kibana version is incompatible with the
|
|||
[Error: Failed to initialize plugins:
|
||||
Incompatible version (incompatible-version, path-3)]
|
||||
`);
|
||||
expect(logger.mockCollect().error).toMatchInlineSnapshot(`
|
||||
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
[Error: Incompatible version (incompatible-version, path-3)],
|
||||
|
@ -230,7 +229,7 @@ test('`start` properly detects plugins that should be disabled.', async () => {
|
|||
expect(mockPluginSystem.startPlugins).toHaveBeenCalledTimes(1);
|
||||
expect(mockPluginSystem.startPlugins).toHaveBeenCalledWith(startDeps);
|
||||
|
||||
expect(logger.mockCollect().info).toMatchInlineSnapshot(`
|
||||
expect(loggingServiceMock.collect(logger).info).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"Plugin \\"explicitly-disabled-plugin\\" is disabled.",
|
||||
|
@ -311,7 +310,7 @@ test('`start` properly invokes `discover` and ignores non-critical errors.', asy
|
|||
{ env, logger, configService }
|
||||
);
|
||||
|
||||
const logs = logger.mockCollect();
|
||||
const logs = loggingServiceMock.collect(logger);
|
||||
expect(logs.info).toHaveLength(0);
|
||||
expect(logs.error).toHaveLength(0);
|
||||
});
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, first, mergeMap, tap, toArray } from 'rxjs/operators';
|
||||
import { CoreContext, CoreService } from '../../types';
|
||||
import { CoreService } from '../../types';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { ElasticsearchServiceStart } from '../elasticsearch';
|
||||
import { Logger } from '../logging';
|
||||
import { discover, PluginDiscoveryError, PluginDiscoveryErrorType } from './discovery';
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreContext } from '../../types';
|
||||
import { CoreContext } from '../core_context';
|
||||
|
||||
const mockCreatePluginStartContext = jest.fn();
|
||||
jest.mock('./plugin_context', () => ({
|
||||
|
@ -27,11 +27,12 @@ jest.mock('./plugin_context', () => ({
|
|||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Config, ConfigService, Env, ObjectToConfigAdapter } from '../config';
|
||||
import { getEnvOptions } from '../config/__mocks__/env';
|
||||
import { ElasticsearchServiceStart } from '../elasticsearch';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { Plugin, PluginName } from './plugin';
|
||||
import { PluginsSystem } from './plugins_system';
|
||||
|
||||
const logger = loggingServiceMock.create();
|
||||
function createPlugin(
|
||||
id: string,
|
||||
{
|
||||
|
@ -60,10 +61,9 @@ let pluginsSystem: PluginsSystem;
|
|||
let configService: ConfigService;
|
||||
let env: Env;
|
||||
let coreContext: CoreContext;
|
||||
let startDeps: { elasticsearch: ElasticsearchServiceStart };
|
||||
const startDeps = { elasticsearch: elasticsearchServiceMock.createStartContract() };
|
||||
beforeEach(() => {
|
||||
env = Env.createDefault(getEnvOptions());
|
||||
startDeps = { elasticsearch: { legacy: {} } as any };
|
||||
|
||||
configService = new ConfigService(
|
||||
new BehaviorSubject<Config>(new ObjectToConfigAdapter({ plugins: { initialize: true } })),
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreContext } from '../../types';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { Logger } from '../logging';
|
||||
import { Plugin, PluginName } from './plugin';
|
||||
import { createPluginStartContext } from './plugin_context';
|
||||
|
|
|
@ -17,14 +17,16 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const mockLoggingService = { asLoggerFactory: jest.fn(), upgrade: jest.fn(), stop: jest.fn() };
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
const logger = loggingServiceMock.create();
|
||||
jest.mock('../logging', () => ({
|
||||
LoggingService: jest.fn(() => mockLoggingService),
|
||||
LoggingService: jest.fn(() => logger),
|
||||
}));
|
||||
|
||||
const mockConfigService = { atPath: jest.fn(), getConfig$: jest.fn() };
|
||||
import { configServiceMock } from '../config/config_service.mock';
|
||||
const configService = configServiceMock.create();
|
||||
jest.mock('../config/config_service', () => ({
|
||||
ConfigService: jest.fn(() => mockConfigService),
|
||||
ConfigService: jest.fn(() => configService),
|
||||
}));
|
||||
|
||||
const mockServer = { start: jest.fn(), stop: jest.fn() };
|
||||
|
@ -33,31 +35,28 @@ jest.mock('../', () => ({ Server: jest.fn(() => mockServer) }));
|
|||
import { BehaviorSubject } from 'rxjs';
|
||||
import { filter, first } from 'rxjs/operators';
|
||||
import { Root } from '.';
|
||||
import { Config, Env } from '../config';
|
||||
import { Env } from '../config';
|
||||
import { getEnvOptions } from '../config/__mocks__/env';
|
||||
import { logger } from '../logging/__mocks__';
|
||||
|
||||
const env = new Env('.', getEnvOptions());
|
||||
const config$ = new BehaviorSubject({} as Config);
|
||||
const config$ = configService.getConfig$();
|
||||
|
||||
let mockConsoleError: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(global.process, 'exit').mockReturnValue(undefined as never);
|
||||
mockConsoleError = jest.spyOn(console, 'error').mockReturnValue(undefined);
|
||||
mockLoggingService.asLoggerFactory.mockReturnValue(logger);
|
||||
mockConfigService.getConfig$.mockReturnValue(new BehaviorSubject({}));
|
||||
mockConfigService.atPath.mockReturnValue(new BehaviorSubject({ someValue: 'foo' }));
|
||||
configService.atPath.mockReturnValue(new BehaviorSubject({ someValue: 'foo' }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
logger.asLoggerFactory.mockClear();
|
||||
logger.stop.mockClear();
|
||||
configService.getConfig$.mockClear();
|
||||
|
||||
mockLoggingService.upgrade.mockReset();
|
||||
mockLoggingService.stop.mockReset();
|
||||
mockLoggingService.asLoggerFactory.mockReset();
|
||||
mockConfigService.atPath.mockReset();
|
||||
mockConfigService.getConfig$.mockReset();
|
||||
logger.upgrade.mockReset();
|
||||
configService.atPath.mockReset();
|
||||
mockServer.start.mockReset();
|
||||
mockServer.stop.mockReset();
|
||||
});
|
||||
|
@ -65,31 +64,31 @@ afterEach(() => {
|
|||
test('starts services on "start"', async () => {
|
||||
const root = new Root(config$, env);
|
||||
|
||||
expect(mockLoggingService.upgrade).not.toHaveBeenCalled();
|
||||
expect(logger.upgrade).not.toHaveBeenCalled();
|
||||
expect(mockServer.start).not.toHaveBeenCalled();
|
||||
|
||||
await root.start();
|
||||
|
||||
expect(mockLoggingService.upgrade).toHaveBeenCalledTimes(1);
|
||||
expect(mockLoggingService.upgrade).toHaveBeenLastCalledWith({ someValue: 'foo' });
|
||||
expect(logger.upgrade).toHaveBeenCalledTimes(1);
|
||||
expect(logger.upgrade).toHaveBeenLastCalledWith({ someValue: 'foo' });
|
||||
expect(mockServer.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('upgrades logging configuration after start', async () => {
|
||||
const mockLoggingConfig$ = new BehaviorSubject({ someValue: 'foo' });
|
||||
mockConfigService.atPath.mockReturnValue(mockLoggingConfig$);
|
||||
configService.atPath.mockReturnValue(mockLoggingConfig$);
|
||||
|
||||
const root = new Root(config$, env);
|
||||
await root.start();
|
||||
|
||||
expect(mockLoggingService.upgrade).toHaveBeenCalledTimes(1);
|
||||
expect(mockLoggingService.upgrade).toHaveBeenLastCalledWith({ someValue: 'foo' });
|
||||
mockLoggingService.upgrade.mockClear();
|
||||
expect(logger.upgrade).toHaveBeenCalledTimes(1);
|
||||
expect(logger.upgrade).toHaveBeenLastCalledWith({ someValue: 'foo' });
|
||||
logger.upgrade.mockClear();
|
||||
|
||||
mockLoggingConfig$.next({ someValue: 'bar' });
|
||||
|
||||
expect(mockLoggingService.upgrade).toHaveBeenCalledTimes(1);
|
||||
expect(mockLoggingService.upgrade).toHaveBeenLastCalledWith({ someValue: 'bar' });
|
||||
expect(logger.upgrade).toHaveBeenCalledTimes(1);
|
||||
expect(logger.upgrade).toHaveBeenLastCalledWith({ someValue: 'bar' });
|
||||
});
|
||||
|
||||
test('stops services on "shutdown"', async () => {
|
||||
|
@ -99,14 +98,14 @@ test('stops services on "shutdown"', async () => {
|
|||
await root.start();
|
||||
|
||||
expect(mockOnShutdown).not.toHaveBeenCalled();
|
||||
expect(mockLoggingService.stop).not.toHaveBeenCalled();
|
||||
expect(logger.stop).not.toHaveBeenCalled();
|
||||
expect(mockServer.stop).not.toHaveBeenCalled();
|
||||
|
||||
await root.shutdown();
|
||||
|
||||
expect(mockOnShutdown).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnShutdown).toHaveBeenCalledWith(undefined);
|
||||
expect(mockLoggingService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(logger.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockServer.stop).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
@ -117,7 +116,7 @@ test('stops services on "shutdown" an calls `onShutdown` with error passed to `s
|
|||
await root.start();
|
||||
|
||||
expect(mockOnShutdown).not.toHaveBeenCalled();
|
||||
expect(mockLoggingService.stop).not.toHaveBeenCalled();
|
||||
expect(logger.stop).not.toHaveBeenCalled();
|
||||
expect(mockServer.stop).not.toHaveBeenCalled();
|
||||
|
||||
const someFatalError = new Error('some fatal error');
|
||||
|
@ -125,7 +124,7 @@ test('stops services on "shutdown" an calls `onShutdown` with error passed to `s
|
|||
|
||||
expect(mockOnShutdown).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnShutdown).toHaveBeenCalledWith(someFatalError);
|
||||
expect(mockLoggingService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(logger.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockServer.stop).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
@ -137,14 +136,14 @@ test('fails and stops services if server fails to start', async () => {
|
|||
mockServer.start.mockRejectedValue(serverError);
|
||||
|
||||
expect(mockOnShutdown).not.toHaveBeenCalled();
|
||||
expect(mockLoggingService.stop).not.toHaveBeenCalled();
|
||||
expect(logger.stop).not.toHaveBeenCalled();
|
||||
expect(mockServer.stop).not.toHaveBeenCalled();
|
||||
|
||||
await expect(root.start()).rejects.toThrowError('server failed');
|
||||
|
||||
expect(mockOnShutdown).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnShutdown).toHaveBeenCalledWith(serverError);
|
||||
expect(mockLoggingService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(logger.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockServer.stop).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
@ -153,12 +152,12 @@ test('fails and stops services if initial logger upgrade fails', async () => {
|
|||
const root = new Root(config$, env, mockOnShutdown);
|
||||
|
||||
const loggingUpgradeError = new Error('logging config upgrade failed');
|
||||
mockLoggingService.upgrade.mockImplementation(() => {
|
||||
logger.upgrade.mockImplementation(() => {
|
||||
throw loggingUpgradeError;
|
||||
});
|
||||
|
||||
expect(mockOnShutdown).not.toHaveBeenCalled();
|
||||
expect(mockLoggingService.stop).not.toHaveBeenCalled();
|
||||
expect(logger.stop).not.toHaveBeenCalled();
|
||||
expect(mockServer.start).not.toHaveBeenCalled();
|
||||
|
||||
await expect(root.start()).rejects.toThrowError('logging config upgrade failed');
|
||||
|
@ -166,30 +165,30 @@ test('fails and stops services if initial logger upgrade fails', async () => {
|
|||
expect(mockOnShutdown).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnShutdown).toHaveBeenCalledWith(loggingUpgradeError);
|
||||
expect(mockServer.start).not.toHaveBeenCalled();
|
||||
expect(mockLoggingService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(logger.stop).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(mockConsoleError.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('stops services if consequent logger upgrade fails', async () => {
|
||||
const onShutdown = new BehaviorSubject<string | null>(null);
|
||||
const mockOnShutdown = jest.fn<any, any>(() => {
|
||||
const mockOnShutdown = jest.fn(() => {
|
||||
onShutdown.next('completed');
|
||||
onShutdown.complete();
|
||||
});
|
||||
|
||||
const mockLoggingConfig$ = new BehaviorSubject({ someValue: 'foo' });
|
||||
mockConfigService.atPath.mockReturnValue(mockLoggingConfig$);
|
||||
configService.atPath.mockReturnValue(mockLoggingConfig$);
|
||||
|
||||
const root = new Root(config$, env, mockOnShutdown);
|
||||
await root.start();
|
||||
|
||||
expect(mockOnShutdown).not.toHaveBeenCalled();
|
||||
expect(mockLoggingService.stop).not.toHaveBeenCalled();
|
||||
expect(logger.stop).not.toHaveBeenCalled();
|
||||
expect(mockServer.stop).not.toHaveBeenCalled();
|
||||
|
||||
const loggingUpgradeError = new Error('logging config consequent upgrade failed');
|
||||
mockLoggingService.upgrade.mockImplementation(() => {
|
||||
logger.upgrade.mockImplementation(() => {
|
||||
throw loggingUpgradeError;
|
||||
});
|
||||
mockLoggingConfig$.next({ someValue: 'bar' });
|
||||
|
@ -204,7 +203,7 @@ test('stops services if consequent logger upgrade fails', async () => {
|
|||
|
||||
expect(mockOnShutdown).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnShutdown).toHaveBeenCalledWith(loggingUpgradeError);
|
||||
expect(mockLoggingService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(logger.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockServer.stop).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(mockConsoleError.mock.calls).toMatchSnapshot();
|
||||
|
|
|
@ -17,5 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { CoreContext } from './core_context';
|
||||
export { CoreService } from './core_service';
|
||||
/**
|
||||
* Use * syntax so that these exports do not break when internal
|
||||
* types are stripped.
|
||||
*
|
||||
* No imports in this directory can import from ./server or ./public
|
||||
* or else builds will not work correctly for both NodeJS and Webpack.
|
||||
*/
|
||||
export * from './core_service';
|
||||
|
|
|
@ -54,8 +54,11 @@ export const TranspileTypescriptTask = {
|
|||
const projects = [
|
||||
typesProjectRepo.tsConfigPath,
|
||||
typesProjectBuild.tsConfigPath,
|
||||
// Browser needs to be compiled before server code so that any shared code
|
||||
// is compiled to the lowest common denominator (server's CommonJS format)
|
||||
// which can be supported by both environments.
|
||||
browserProject.tsConfigPath,
|
||||
defaultProject.tsConfigPath,
|
||||
browserProject.tsConfigPath
|
||||
];
|
||||
|
||||
// compile each typescript config file
|
||||
|
|
|
@ -84,7 +84,6 @@ fi
|
|||
### "install" node into this shell
|
||||
###
|
||||
export PATH="$nodeBin:$PATH"
|
||||
hash -r
|
||||
|
||||
###
|
||||
### downloading yarn
|
||||
|
@ -102,7 +101,6 @@ yarn config set yarn-offline-mirror "$cacheDir/yarn-offline-cache"
|
|||
###
|
||||
yarnGlobalDir="$(yarn global bin)"
|
||||
export PATH="$PATH:$yarnGlobalDir"
|
||||
hash -r
|
||||
|
||||
###
|
||||
### use the chromedriver cache if it exists
|
||||
|
|
|
@ -53,7 +53,6 @@ export default {
|
|||
'<rootDir>/src/dev/jest/setup/babel_polyfill.js',
|
||||
'<rootDir>/src/dev/jest/setup/polyfills.js',
|
||||
'<rootDir>/src/dev/jest/setup/enzyme.js',
|
||||
'<rootDir>/src/dev/jest/setup/throw_on_console_error.js',
|
||||
],
|
||||
coverageDirectory: '<rootDir>/target/jest-coverage',
|
||||
coverageReporters: [
|
||||
|
|
|
@ -21,18 +21,23 @@ import { inspect } from 'util';
|
|||
|
||||
const FAIL_TAG = Symbol('fail error');
|
||||
|
||||
export function createFailError(reason, exitCode = 1) {
|
||||
const error = new Error(reason);
|
||||
error.exitCode = exitCode;
|
||||
error[FAIL_TAG] = true;
|
||||
return error;
|
||||
interface FailError extends Error {
|
||||
exitCode: number;
|
||||
[FAIL_TAG]: true;
|
||||
}
|
||||
|
||||
export function isFailError(error) {
|
||||
export function createFailError(reason: string, exitCode = 1): FailError {
|
||||
return Object.assign(new Error(reason), {
|
||||
exitCode,
|
||||
[FAIL_TAG]: true as true,
|
||||
});
|
||||
}
|
||||
|
||||
export function isFailError(error: any): error is FailError {
|
||||
return Boolean(error && error[FAIL_TAG]);
|
||||
}
|
||||
|
||||
export function combineErrors(errors) {
|
||||
export function combineErrors(errors: Array<Error | FailError>) {
|
||||
if (errors.length === 1) {
|
||||
return errors[0];
|
||||
}
|
|
@ -21,8 +21,19 @@ import { relative } from 'path';
|
|||
|
||||
import getopts from 'getopts';
|
||||
|
||||
export function getFlags(argv) {
|
||||
return getopts(argv, {
|
||||
export interface Flags {
|
||||
verbose: boolean;
|
||||
quiet: boolean;
|
||||
silent: boolean;
|
||||
debug: boolean;
|
||||
help: boolean;
|
||||
_: string[];
|
||||
|
||||
[key: string]: undefined | boolean | string | string[];
|
||||
}
|
||||
|
||||
export function getFlags(argv: string[]): Flags {
|
||||
const { verbose, quiet, silent, debug, help, _, ...others } = getopts(argv, {
|
||||
alias: {
|
||||
v: 'verbose',
|
||||
},
|
||||
|
@ -32,14 +43,23 @@ export function getFlags(argv) {
|
|||
silent: false,
|
||||
debug: false,
|
||||
help: false,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
verbose,
|
||||
quiet,
|
||||
silent,
|
||||
debug,
|
||||
help,
|
||||
_,
|
||||
...others,
|
||||
};
|
||||
}
|
||||
|
||||
export function getHelp() {
|
||||
return (
|
||||
`
|
||||
node ${relative(process.cwd(), process.argv[1], '.js')}
|
||||
return `
|
||||
node ${relative(process.cwd(), process.argv[1])}
|
||||
|
||||
Runs a dev task
|
||||
|
||||
|
@ -50,6 +70,5 @@ export function getHelp() {
|
|||
--silent Don't log anything
|
||||
--help Show this message
|
||||
|
||||
`
|
||||
);
|
||||
`;
|
||||
}
|
25
src/dev/run/index.d.ts
vendored
25
src/dev/run/index.d.ts
vendored
|
@ -1,25 +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 { ToolingLog } from '@kbn/dev-utils';
|
||||
|
||||
export function createFailError(msg: string, exitCode?: number): Error;
|
||||
export function run(
|
||||
body: (args: { flags: Record<string, any>; log: ToolingLog }) => void
|
||||
): Promise<void>;
|
|
@ -17,11 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ToolingLog, pickLevelFromFlags } from '@kbn/dev-utils';
|
||||
import { pickLevelFromFlags, ToolingLog } from '@kbn/dev-utils';
|
||||
import { isFailError } from './fail';
|
||||
import { getFlags, getHelp } from './flags';
|
||||
import { Flags, getFlags, getHelp } from './flags';
|
||||
|
||||
export async function run(body) {
|
||||
export async function run(body: (args: { log: ToolingLog; flags: Flags }) => Promise<void> | void) {
|
||||
const flags = getFlags(process.argv.slice(2));
|
||||
|
||||
if (flags.help) {
|
||||
|
@ -31,7 +31,7 @@ export async function run(body) {
|
|||
|
||||
const log = new ToolingLog({
|
||||
level: pickLevelFromFlags(flags),
|
||||
writeTo: process.stdout
|
||||
writeTo: process.stdout,
|
||||
});
|
||||
|
||||
try {
|
|
@ -49,6 +49,16 @@ run(
|
|||
);
|
||||
}
|
||||
|
||||
if (typeof path === 'boolean' || typeof includeConfig === 'boolean') {
|
||||
throw createFailError(
|
||||
`${chalk.white.bgRed(' I18N ERROR ')} --path and --include-config require a value`
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof fix !== 'boolean') {
|
||||
throw createFailError(`${chalk.white.bgRed(' I18N ERROR ')} --fix can't have a value`);
|
||||
}
|
||||
|
||||
const config = await mergeConfigs(includeConfig);
|
||||
const defaultMessages = await extractDefaultMessages({ path, config });
|
||||
|
||||
|
|
|
@ -39,6 +39,12 @@ run(
|
|||
);
|
||||
}
|
||||
|
||||
if (typeof path === 'boolean' || typeof includeConfig === 'boolean') {
|
||||
throw createFailError(
|
||||
`${chalk.white.bgRed(' I18N ERROR ')} --path and --include-config require a value`
|
||||
);
|
||||
}
|
||||
|
||||
const config = await mergeConfigs(includeConfig);
|
||||
const defaultMessages = await extractDefaultMessages({ path, config });
|
||||
|
||||
|
|
|
@ -47,9 +47,30 @@ run(
|
|||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(target)) {
|
||||
if (typeof target === 'boolean' || Array.isArray(target)) {
|
||||
throw createFailError(
|
||||
`${chalk.white.bgRed(' I18N ERROR ')} --target should be specified only once.`
|
||||
`${chalk.white.bgRed(
|
||||
' I18N ERROR '
|
||||
)} --target should be specified only once and must have a value.`
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof path === 'boolean' || typeof includeConfig === 'boolean') {
|
||||
throw createFailError(
|
||||
`${chalk.white.bgRed(' I18N ERROR ')} --path and --include-config require a value`
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof ignoreIncompatible !== 'boolean' ||
|
||||
typeof ignoreUnused !== 'boolean' ||
|
||||
typeof ignoreMissing !== 'boolean' ||
|
||||
typeof dryRun !== 'boolean'
|
||||
) {
|
||||
throw createFailError(
|
||||
`${chalk.white.bgRed(
|
||||
' I18N ERROR '
|
||||
)} --ignore-incompatible, --ignore-unused, --ignore-missing, and --dry-run can't have values`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ export default function (kibana) {
|
|||
}
|
||||
|
||||
const config = server.config();
|
||||
const legacyEsConfig = await server.core.elasticsearch.legacy.config$.pipe(first()).toPromise();
|
||||
const legacyEsConfig = await server.newPlatform.start.core.elasticsearch.legacy.config$.pipe(first()).toPromise();
|
||||
const proxyConfigCollection = new ProxyConfigCollection(options.proxyConfig);
|
||||
const proxyPathFilters = options.proxyFilter.map(str => new RegExp(str));
|
||||
|
||||
|
|
|
@ -36,9 +36,9 @@ export default function (kibana) {
|
|||
// value from all observables here to be able to synchronously return and create
|
||||
// cluster clients afterwards.
|
||||
const [esConfig, adminCluster, dataCluster] = await combineLatest(
|
||||
server.core.elasticsearch.legacy.config$,
|
||||
server.core.elasticsearch.adminClient$,
|
||||
server.core.elasticsearch.dataClient$
|
||||
server.newPlatform.start.core.elasticsearch.legacy.config$,
|
||||
server.newPlatform.start.core.elasticsearch.adminClient$,
|
||||
server.newPlatform.start.core.elasticsearch.dataClient$
|
||||
).pipe(
|
||||
first(),
|
||||
map(([config, adminClusterClient, dataClusterClient]) => [
|
||||
|
@ -80,7 +80,7 @@ export default function (kibana) {
|
|||
// We fill all the missing properties in the `clientConfig` using the default
|
||||
// Elasticsearch config so that we don't depend on default values set and
|
||||
// controlled by underlying Elasticsearch JS client.
|
||||
const cluster = new Cluster(server.core.elasticsearch.createClient(name, {
|
||||
const cluster = new Cluster(server.newPlatform.start.core.elasticsearch.createClient(name, {
|
||||
...esConfig,
|
||||
...clientConfig,
|
||||
}));
|
||||
|
|
|
@ -94,6 +94,7 @@ exports[`renders ControlsTab 1`] = `
|
|||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
id="selectControlType"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
aria-label="Select control type"
|
||||
|
@ -127,6 +128,7 @@ exports[`renders ControlsTab 1`] = `
|
|||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
id="addControl"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiButton
|
||||
aria-label="Add control"
|
||||
|
|
|
@ -28,6 +28,7 @@ exports[`renders dynamic options should display disabled dynamic options with to
|
|||
}
|
||||
id="multiselect-0"
|
||||
key="multiselect"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
|
@ -55,6 +56,7 @@ exports[`renders dynamic options should display disabled dynamic options with to
|
|||
}
|
||||
id="dynamicOptions-0"
|
||||
key="dynamicOptions"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
|
@ -90,6 +92,7 @@ exports[`renders dynamic options should display disabled dynamic options with to
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
compressed={false}
|
||||
|
@ -132,6 +135,7 @@ exports[`renders dynamic options should display dynamic options for string field
|
|||
}
|
||||
id="multiselect-0"
|
||||
key="multiselect"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
|
@ -159,6 +163,7 @@ exports[`renders dynamic options should display dynamic options for string field
|
|||
}
|
||||
id="dynamicOptions-0"
|
||||
key="dynamicOptions"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
|
@ -205,6 +210,7 @@ exports[`renders dynamic options should display size field when dynamic options
|
|||
}
|
||||
id="multiselect-0"
|
||||
key="multiselect"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
|
@ -232,6 +238,7 @@ exports[`renders dynamic options should display size field when dynamic options
|
|||
}
|
||||
id="dynamicOptions-0"
|
||||
key="dynamicOptions"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
|
@ -267,6 +274,7 @@ exports[`renders dynamic options should display size field when dynamic options
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
compressed={false}
|
||||
|
@ -316,6 +324,7 @@ exports[`renders should display chaining input when parents are provided 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
compressed={false}
|
||||
|
@ -354,6 +363,7 @@ exports[`renders should display chaining input when parents are provided 1`] = `
|
|||
}
|
||||
id="multiselect-0"
|
||||
key="multiselect"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
|
@ -381,6 +391,7 @@ exports[`renders should display chaining input when parents are provided 1`] = `
|
|||
}
|
||||
id="dynamicOptions-0"
|
||||
key="dynamicOptions"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
|
@ -416,6 +427,7 @@ exports[`renders should display chaining input when parents are provided 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
compressed={false}
|
||||
|
|
|
@ -7,6 +7,7 @@ exports[`renders OptionsTab 1`] = `
|
|||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
id="updateFiltersOnChange"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
|
@ -26,6 +27,7 @@ exports[`renders OptionsTab 1`] = `
|
|||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
id="useTimeFilter"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
|
@ -45,6 +47,7 @@ exports[`renders OptionsTab 1`] = `
|
|||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
id="pinFilters"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
data-test-subj="inputControlEditorPinFiltersCheckbox"
|
||||
|
|
|
@ -27,6 +27,7 @@ exports[`renders RangeControlEditor 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
compressed={false}
|
||||
|
@ -49,6 +50,7 @@ exports[`renders RangeControlEditor 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
compressed={false}
|
||||
|
|
|
@ -8,6 +8,7 @@ exports[`renders disabled control with tooltip 1`] = `
|
|||
hasEmptyLabelSpace={false}
|
||||
id="controlId"
|
||||
label="test control"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiToolTip
|
||||
content="I am disabled for testing purposes"
|
||||
|
@ -30,6 +31,7 @@ exports[`renders enabled control 1`] = `
|
|||
hasEmptyLabelSpace={false}
|
||||
id="controlId"
|
||||
label="test control"
|
||||
labelType="label"
|
||||
>
|
||||
<div>
|
||||
My Control
|
||||
|
|
|
@ -13,12 +13,13 @@ exports[`disabled 1`] = `
|
|||
fullWidth={false}
|
||||
levels={Array []}
|
||||
max={100}
|
||||
min={1}
|
||||
min={0}
|
||||
showInput={false}
|
||||
showLabels={false}
|
||||
showRange={false}
|
||||
showTicks={false}
|
||||
showValue={false}
|
||||
step={1}
|
||||
/>
|
||||
</FormRow>
|
||||
`;
|
||||
|
@ -37,6 +38,7 @@ exports[`renders RangeControl 1`] = `
|
|||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
isInvalid={false}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
|
|
|
@ -30,9 +30,7 @@ exports[`renders DashboardCloneModal 1`] = `
|
|||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiFieldText
|
||||
autoFocus={true}
|
||||
compressed={false}
|
||||
|
|
|
@ -18,6 +18,7 @@ exports[`renders DashboardSaveModal 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiTextArea
|
||||
compressed={true}
|
||||
|
@ -46,6 +47,7 @@ exports[`renders DashboardSaveModal 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
|
|
|
@ -45,9 +45,7 @@ exports[`apmUiEnabled 1`] = `
|
|||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="homeAddData__flexGroup"
|
||||
|
@ -84,7 +82,6 @@ exports[`apmUiEnabled 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="apmApp"
|
||||
/>
|
||||
}
|
||||
|
@ -120,7 +117,6 @@ exports[`apmUiEnabled 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="loggingApp"
|
||||
/>
|
||||
}
|
||||
|
@ -156,7 +152,6 @@ exports[`apmUiEnabled 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="monitoringApp"
|
||||
/>
|
||||
}
|
||||
|
@ -192,7 +187,6 @@ exports[`apmUiEnabled 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="securityApp"
|
||||
/>
|
||||
}
|
||||
|
@ -203,10 +197,7 @@ exports[`apmUiEnabled 1`] = `
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule
|
||||
margin="l"
|
||||
size="full"
|
||||
/>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGrid
|
||||
columns={2}
|
||||
gutterSize="l"
|
||||
|
@ -343,9 +334,7 @@ exports[`isNewKibanaInstance 1`] = `
|
|||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="homeAddData__flexGroup"
|
||||
|
@ -382,7 +371,6 @@ exports[`isNewKibanaInstance 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="loggingApp"
|
||||
/>
|
||||
}
|
||||
|
@ -418,7 +406,6 @@ exports[`isNewKibanaInstance 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="monitoringApp"
|
||||
/>
|
||||
}
|
||||
|
@ -454,7 +441,6 @@ exports[`isNewKibanaInstance 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="securityApp"
|
||||
/>
|
||||
}
|
||||
|
@ -465,10 +451,7 @@ exports[`isNewKibanaInstance 1`] = `
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule
|
||||
margin="l"
|
||||
size="full"
|
||||
/>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGrid
|
||||
columns={2}
|
||||
gutterSize="l"
|
||||
|
@ -605,9 +588,7 @@ exports[`mlEnabled 1`] = `
|
|||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="homeAddData__flexGroup"
|
||||
|
@ -644,7 +625,6 @@ exports[`mlEnabled 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="apmApp"
|
||||
/>
|
||||
}
|
||||
|
@ -680,7 +660,6 @@ exports[`mlEnabled 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="loggingApp"
|
||||
/>
|
||||
}
|
||||
|
@ -716,7 +695,6 @@ exports[`mlEnabled 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="monitoringApp"
|
||||
/>
|
||||
}
|
||||
|
@ -752,7 +730,6 @@ exports[`mlEnabled 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="securityApp"
|
||||
/>
|
||||
}
|
||||
|
@ -763,10 +740,7 @@ exports[`mlEnabled 1`] = `
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule
|
||||
margin="l"
|
||||
size="full"
|
||||
/>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGrid
|
||||
columns={3}
|
||||
gutterSize="l"
|
||||
|
@ -944,9 +918,7 @@ exports[`render 1`] = `
|
|||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="homeAddData__flexGroup"
|
||||
|
@ -983,7 +955,6 @@ exports[`render 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="loggingApp"
|
||||
/>
|
||||
}
|
||||
|
@ -1019,7 +990,6 @@ exports[`render 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="monitoringApp"
|
||||
/>
|
||||
}
|
||||
|
@ -1055,7 +1025,6 @@ exports[`render 1`] = `
|
|||
icon={
|
||||
<EuiIcon
|
||||
className="homAddData__icon"
|
||||
size="m"
|
||||
type="securityApp"
|
||||
/>
|
||||
}
|
||||
|
@ -1066,10 +1035,7 @@ exports[`render 1`] = `
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule
|
||||
margin="l"
|
||||
size="full"
|
||||
/>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGrid
|
||||
columns={2}
|
||||
gutterSize="l"
|
||||
|
|
|
@ -86,7 +86,6 @@ exports[`render 1`] = `
|
|||
>
|
||||
<EuiIcon
|
||||
color="subdued"
|
||||
size="m"
|
||||
type="dot"
|
||||
/>
|
||||
</EuiText>
|
||||
|
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
exports[`render 1`] = `
|
||||
<div>
|
||||
<EuiHorizontalRule
|
||||
margin="l"
|
||||
size="full"
|
||||
/>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
component="div"
|
||||
|
|
|
@ -42,9 +42,7 @@ exports[`props exportedFieldsUrl 1`] = `
|
|||
text="this is a great tutorial about..."
|
||||
/>
|
||||
<div>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill={false}
|
||||
|
|
|
@ -553,7 +553,6 @@ exports[`bulkCreate should display success message when bulkCreate is successful
|
|||
>
|
||||
<EuiIcon
|
||||
className="euiStepNumber__icon"
|
||||
size="m"
|
||||
title="complete"
|
||||
type="check"
|
||||
>
|
||||
|
|
|
@ -12,15 +12,11 @@ exports[`isCloudEnabled is false should not render instruction toggle when ON_PR
|
|||
description="tutorial used to drive jest tests"
|
||||
title="jest test tutorial"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<div
|
||||
className="eui-textCenter"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
|
@ -67,9 +63,7 @@ exports[`isCloudEnabled is false should render ON_PREM instructions with instruc
|
|||
iconType="logoApache"
|
||||
title="jest test tutorial"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<div
|
||||
className="eui-textCenter"
|
||||
>
|
||||
|
@ -109,9 +103,7 @@ exports[`isCloudEnabled is false should render ON_PREM instructions with instruc
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
|
@ -158,15 +150,11 @@ exports[`should render ELASTIC_CLOUD instructions when isCloudEnabled is true 1`
|
|||
iconType="logoApache"
|
||||
title="jest test tutorial"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<div
|
||||
className="eui-textCenter"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
|
|
|
@ -80,6 +80,7 @@ exports[`Header should mark the input as invalid 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
compressed={false}
|
||||
|
@ -196,6 +197,7 @@ exports[`Header should render normally 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
compressed={false}
|
||||
|
|
|
@ -11,12 +11,22 @@ exports[`IndicesList should change pages 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
kibana
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -25,12 +35,22 @@ exports[`IndicesList should change pages 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
es
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -138,12 +158,22 @@ exports[`IndicesList should change per page 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
kibana
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -261,6 +291,11 @@ exports[`IndicesList should highlight the query in the matches 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
<span>
|
||||
|
@ -272,6 +307,11 @@ exports[`IndicesList should highlight the query in the matches 1`] = `
|
|||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -280,12 +320,22 @@ exports[`IndicesList should highlight the query in the matches 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
es
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -393,12 +443,22 @@ exports[`IndicesList should render normally 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
kibana
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -407,12 +467,22 @@ exports[`IndicesList should render normally 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
es
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -520,12 +590,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
kibana
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -534,12 +614,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
es
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -548,12 +638,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
kibana
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -562,12 +662,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
es
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -576,12 +686,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
kibana
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -590,12 +710,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
es
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -604,12 +734,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
kibana
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -618,12 +758,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
es
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -632,12 +782,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
kibana
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
@ -646,12 +806,22 @@ exports[`IndicesList updating props should render all new indices 1`] = `
|
|||
>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
>
|
||||
es
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell
|
||||
align="left"
|
||||
mobileOptions={
|
||||
Object {
|
||||
"show": true,
|
||||
}
|
||||
}
|
||||
textOnly={true}
|
||||
/>
|
||||
</EuiTableRow>
|
||||
|
|
|
@ -11,7 +11,6 @@ exports[`StatusMessage should render with exact matches 1`] = `
|
|||
component="span"
|
||||
>
|
||||
<EuiIcon
|
||||
size="m"
|
||||
type="check"
|
||||
/>
|
||||
<span>
|
||||
|
|
|
@ -56,6 +56,7 @@ exports[`TimeField should render a loading state 1`] = `
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
compressed={false}
|
||||
|
@ -143,6 +144,7 @@ exports[`TimeField should render a selected time field 1`] = `
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
compressed={false}
|
||||
|
@ -232,6 +234,7 @@ exports[`TimeField should render normally 1`] = `
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
compressed={false}
|
||||
|
|
|
@ -9,9 +9,7 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = `
|
|||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={false}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
|
@ -202,9 +200,7 @@ exports[`Flyout should render import step 1`] = `
|
|||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={false}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
|
@ -231,6 +227,7 @@ exports[`Flyout should render import step 1`] = `
|
|||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFilePicker
|
||||
compressed={false}
|
||||
|
@ -248,6 +245,7 @@ exports[`Flyout should render import step 1`] = `
|
|||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
|
|
|
@ -9,9 +9,7 @@ exports[`Relationships should render dashboards normally 1`] = `
|
|||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={false}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
|
@ -72,7 +70,7 @@ exports[`Relationships should render dashboards normally 1`] = `
|
|||
Array [
|
||||
Object {
|
||||
"render": [Function],
|
||||
"width": 24,
|
||||
"width": "24px",
|
||||
},
|
||||
Object {
|
||||
"field": "title",
|
||||
|
@ -121,9 +119,7 @@ exports[`Relationships should render errors 1`] = `
|
|||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={false}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
|
@ -172,9 +168,7 @@ exports[`Relationships should render index patterns normally 1`] = `
|
|||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={false}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
|
@ -197,9 +191,129 @@ exports[`Relationships should render index patterns normally 1`] = `
|
|||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<EuiLoadingKibana
|
||||
size="xl"
|
||||
/>
|
||||
<EuiDescriptionList
|
||||
align="left"
|
||||
compressed={false}
|
||||
textStyle="normal"
|
||||
type="row"
|
||||
>
|
||||
<EuiDescriptionListTitle
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "1rem",
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
size="m"
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Warning"
|
||||
id="kbn.management.objects.objectsTable.relationships.warningTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p />
|
||||
</EuiCallOut>
|
||||
</EuiDescriptionListTitle>
|
||||
<EuiInMemoryTable
|
||||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"render": [Function],
|
||||
"width": "24px",
|
||||
},
|
||||
Object {
|
||||
"field": "title",
|
||||
"name": "Title",
|
||||
"render": [Function],
|
||||
},
|
||||
Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"description": "View this saved object within Kibana",
|
||||
"icon": "eye",
|
||||
"name": "In app",
|
||||
"onClick": [Function],
|
||||
},
|
||||
],
|
||||
"name": "Actions",
|
||||
},
|
||||
]
|
||||
}
|
||||
executeQueryOptions={Object {}}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
},
|
||||
]
|
||||
}
|
||||
pagination={true}
|
||||
responsive={true}
|
||||
sorting={false}
|
||||
/>
|
||||
<EuiDescriptionListTitle
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "1rem",
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
size="m"
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Warning"
|
||||
id="kbn.management.objects.objectsTable.relationships.warningTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p />
|
||||
</EuiCallOut>
|
||||
</EuiDescriptionListTitle>
|
||||
<EuiInMemoryTable
|
||||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"render": [Function],
|
||||
"width": "24px",
|
||||
},
|
||||
Object {
|
||||
"field": "title",
|
||||
"name": "Title",
|
||||
"render": [Function],
|
||||
},
|
||||
Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"description": "View this saved object within Kibana",
|
||||
"icon": "eye",
|
||||
"name": "In app",
|
||||
"onClick": [Function],
|
||||
},
|
||||
],
|
||||
"name": "Actions",
|
||||
},
|
||||
]
|
||||
}
|
||||
executeQueryOptions={Object {}}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"id": "2",
|
||||
},
|
||||
]
|
||||
}
|
||||
pagination={true}
|
||||
responsive={true}
|
||||
sorting={false}
|
||||
/>
|
||||
</EuiDescriptionList>
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
`;
|
||||
|
@ -213,9 +327,7 @@ exports[`Relationships should render searches normally 1`] = `
|
|||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={false}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
|
@ -276,7 +388,7 @@ exports[`Relationships should render searches normally 1`] = `
|
|||
Array [
|
||||
Object {
|
||||
"render": [Function],
|
||||
"width": 24,
|
||||
"width": "24px",
|
||||
},
|
||||
Object {
|
||||
"field": "title",
|
||||
|
@ -340,7 +452,7 @@ exports[`Relationships should render searches normally 1`] = `
|
|||
Array [
|
||||
Object {
|
||||
"render": [Function],
|
||||
"width": 24,
|
||||
"width": "24px",
|
||||
},
|
||||
Object {
|
||||
"field": "title",
|
||||
|
@ -386,9 +498,7 @@ exports[`Relationships should render visualizations normally 1`] = `
|
|||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={false}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
|
@ -449,7 +559,7 @@ exports[`Relationships should render visualizations normally 1`] = `
|
|||
Array [
|
||||
Object {
|
||||
"render": [Function],
|
||||
"width": 24,
|
||||
"width": "24px",
|
||||
},
|
||||
Object {
|
||||
"field": "title",
|
||||
|
|
|
@ -227,7 +227,7 @@ class RelationshipsUI extends Component {
|
|||
items={list}
|
||||
columns={[
|
||||
{
|
||||
width: 24,
|
||||
width: '24px',
|
||||
render: () => (
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
|
|
|
@ -86,6 +86,7 @@ exports[`Field for array setting should render as read only with help text if ov
|
|||
}
|
||||
isInvalid={false}
|
||||
label="array:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="array test setting"
|
||||
|
@ -171,6 +172,7 @@ exports[`Field for array setting should render custom setting icon if it is cust
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="array:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="array test setting"
|
||||
|
@ -245,6 +247,7 @@ exports[`Field for array setting should render default value if there is no user
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="array:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="array test setting"
|
||||
|
@ -361,6 +364,7 @@ exports[`Field for array setting should render user value if there is user value
|
|||
}
|
||||
isInvalid={false}
|
||||
label="array:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="array test setting"
|
||||
|
@ -469,6 +473,7 @@ exports[`Field for boolean setting should render as read only with help text if
|
|||
}
|
||||
isInvalid={false}
|
||||
label="boolean:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
aria-label="boolean test setting"
|
||||
|
@ -558,6 +563,7 @@ exports[`Field for boolean setting should render custom setting icon if it is cu
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="boolean:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
aria-label="boolean test setting"
|
||||
|
@ -636,6 +642,7 @@ exports[`Field for boolean setting should render default value if there is no us
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="boolean:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
aria-label="boolean test setting"
|
||||
|
@ -756,6 +763,7 @@ exports[`Field for boolean setting should render user value if there is user val
|
|||
}
|
||||
isInvalid={false}
|
||||
label="boolean:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
aria-label="boolean test setting"
|
||||
|
@ -868,6 +876,7 @@ exports[`Field for image setting should render as read only with help text if ov
|
|||
}
|
||||
isInvalid={false}
|
||||
label="image:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiImage
|
||||
allowFullScreen={true}
|
||||
|
@ -950,6 +959,7 @@ exports[`Field for image setting should render custom setting icon if it is cust
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="image:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFilePicker
|
||||
accept=".jpg,.jpeg,.png"
|
||||
|
@ -1022,6 +1032,7 @@ exports[`Field for image setting should render default value if there is no user
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="image:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFilePicker
|
||||
accept=".jpg,.jpeg,.png"
|
||||
|
@ -1151,6 +1162,7 @@ exports[`Field for image setting should render user value if there is user value
|
|||
}
|
||||
isInvalid={false}
|
||||
label="image:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiImage
|
||||
allowFullScreen={true}
|
||||
|
@ -1260,6 +1272,7 @@ exports[`Field for json setting should render as read only with help text if ove
|
|||
}
|
||||
isInvalid={false}
|
||||
label="json:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-json:test:setting"
|
||||
|
@ -1283,6 +1296,7 @@ exports[`Field for json setting should render as read only with help text if ove
|
|||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
showGutter={false}
|
||||
theme="textmate"
|
||||
value="{\\"hello\\": \\"world\\"}"
|
||||
width="100%"
|
||||
|
@ -1361,6 +1375,7 @@ exports[`Field for json setting should render custom setting icon if it is custo
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="json:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-json:test:setting"
|
||||
|
@ -1384,6 +1399,7 @@ exports[`Field for json setting should render custom setting icon if it is custo
|
|||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
showGutter={false}
|
||||
theme="textmate"
|
||||
value="{\\"foo\\": \\"bar\\"}"
|
||||
width="100%"
|
||||
|
@ -1497,6 +1513,7 @@ exports[`Field for json setting should render default value if there is no user
|
|||
}
|
||||
isInvalid={false}
|
||||
label="json:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-json:test:setting"
|
||||
|
@ -1520,6 +1537,7 @@ exports[`Field for json setting should render default value if there is no user
|
|||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
showGutter={false}
|
||||
theme="textmate"
|
||||
value="{\\"foo\\": \\"bar\\"}"
|
||||
width="100%"
|
||||
|
@ -1633,6 +1651,7 @@ exports[`Field for json setting should render user value if there is user value
|
|||
}
|
||||
isInvalid={false}
|
||||
label="json:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-json:test:setting"
|
||||
|
@ -1656,6 +1675,7 @@ exports[`Field for json setting should render user value if there is user value
|
|||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
showGutter={false}
|
||||
theme="textmate"
|
||||
value="{\\"hello\\": \\"world\\"}"
|
||||
width="100%"
|
||||
|
@ -1757,6 +1777,7 @@ exports[`Field for markdown setting should render as read only with help text if
|
|||
}
|
||||
isInvalid={false}
|
||||
label="markdown:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-markdown:test:setting"
|
||||
|
@ -1780,6 +1801,7 @@ exports[`Field for markdown setting should render as read only with help text if
|
|||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
showGutter={false}
|
||||
theme="textmate"
|
||||
value="**bold**"
|
||||
width="100%"
|
||||
|
@ -1858,6 +1880,7 @@ exports[`Field for markdown setting should render custom setting icon if it is c
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="markdown:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-markdown:test:setting"
|
||||
|
@ -1881,6 +1904,7 @@ exports[`Field for markdown setting should render custom setting icon if it is c
|
|||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
showGutter={false}
|
||||
theme="textmate"
|
||||
value=""
|
||||
width="100%"
|
||||
|
@ -1948,6 +1972,7 @@ exports[`Field for markdown setting should render default value if there is no u
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="markdown:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-markdown:test:setting"
|
||||
|
@ -1971,6 +1996,7 @@ exports[`Field for markdown setting should render default value if there is no u
|
|||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
showGutter={false}
|
||||
theme="textmate"
|
||||
value=""
|
||||
width="100%"
|
||||
|
@ -2080,6 +2106,7 @@ exports[`Field for markdown setting should render user value if there is user va
|
|||
}
|
||||
isInvalid={false}
|
||||
label="markdown:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<div
|
||||
data-test-subj="advancedSetting-editField-markdown:test:setting"
|
||||
|
@ -2103,6 +2130,7 @@ exports[`Field for markdown setting should render user value if there is user va
|
|||
"tabSize": 2,
|
||||
}
|
||||
}
|
||||
showGutter={false}
|
||||
theme="textmate"
|
||||
value="**bold**"
|
||||
width="100%"
|
||||
|
@ -2204,6 +2232,7 @@ exports[`Field for number setting should render as read only with help text if o
|
|||
}
|
||||
isInvalid={false}
|
||||
label="number:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
aria-label="number test setting"
|
||||
|
@ -2289,6 +2318,7 @@ exports[`Field for number setting should render custom setting icon if it is cus
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="number:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
aria-label="number test setting"
|
||||
|
@ -2363,6 +2393,7 @@ exports[`Field for number setting should render default value if there is no use
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="number:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
aria-label="number test setting"
|
||||
|
@ -2479,6 +2510,7 @@ exports[`Field for number setting should render user value if there is user valu
|
|||
}
|
||||
isInvalid={false}
|
||||
label="number:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldNumber
|
||||
aria-label="number test setting"
|
||||
|
@ -2587,6 +2619,7 @@ exports[`Field for select setting should render as read only with help text if o
|
|||
}
|
||||
isInvalid={false}
|
||||
label="select:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
aria-label="select test setting"
|
||||
|
@ -2689,6 +2722,7 @@ exports[`Field for select setting should render custom setting icon if it is cus
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="select:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
aria-label="select test setting"
|
||||
|
@ -2780,6 +2814,7 @@ exports[`Field for select setting should render default value if there is no use
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="select:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
aria-label="select test setting"
|
||||
|
@ -2913,6 +2948,7 @@ exports[`Field for select setting should render user value if there is user valu
|
|||
}
|
||||
isInvalid={false}
|
||||
label="select:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSelect
|
||||
aria-label="select test setting"
|
||||
|
@ -3038,6 +3074,7 @@ exports[`Field for string setting should render as read only with help text if o
|
|||
}
|
||||
isInvalid={false}
|
||||
label="string:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="string test setting"
|
||||
|
@ -3123,6 +3160,7 @@ exports[`Field for string setting should render custom setting icon if it is cus
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="string:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="string test setting"
|
||||
|
@ -3197,6 +3235,7 @@ exports[`Field for string setting should render default value if there is no use
|
|||
helpText={null}
|
||||
isInvalid={false}
|
||||
label="string:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="string test setting"
|
||||
|
@ -3313,6 +3352,7 @@ exports[`Field for string setting should render user value if there is user valu
|
|||
}
|
||||
isInvalid={false}
|
||||
label="string:test:setting"
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="string test setting"
|
||||
|
|
|
@ -397,7 +397,7 @@ class FieldUI extends PureComponent {
|
|||
editorProps={{
|
||||
$blockScrolling: Infinity
|
||||
}}
|
||||
|
||||
showGutter={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -62,8 +62,6 @@ class TableListViewUi extends React.Component {
|
|||
showLimitError: false,
|
||||
filter: this.props.initialFilter,
|
||||
selectedIds: [],
|
||||
sortField: 'title',
|
||||
sortDirection: 'asc',
|
||||
page: 0,
|
||||
perPage: 20,
|
||||
};
|
||||
|
@ -92,11 +90,12 @@ class TableListViewUi extends React.Component {
|
|||
|
||||
// We need this check to handle the case where search results come back in a different
|
||||
// order than they were sent out. Only load results for the most recent search.
|
||||
// Also, in case filter is empty, items are being pre-sorted alphabetically.
|
||||
if (filter === this.state.filter) {
|
||||
this.setState({
|
||||
hasInitialFetchReturned: true,
|
||||
isFetchingItems: false,
|
||||
items: response.hits,
|
||||
items: (!filter ? _.sortBy(response.hits, 'title') : response.hits),
|
||||
totalItems: response.total,
|
||||
showLimitError: response.total > this.props.listingLimit,
|
||||
});
|
||||
|
@ -336,14 +335,6 @@ class TableListViewUi extends React.Component {
|
|||
onClick: this.props.editItem
|
||||
}];
|
||||
|
||||
const sorting = {};
|
||||
if (this.state.sortField) {
|
||||
sorting.sort = {
|
||||
field: this.state.sortField,
|
||||
direction: this.state.sortDirection,
|
||||
};
|
||||
}
|
||||
|
||||
const search = {
|
||||
onChange: this.setFilter.bind(this),
|
||||
toolsLeft: this.renderToolsLeft(),
|
||||
|
@ -382,7 +373,7 @@ class TableListViewUi extends React.Component {
|
|||
message={noItemsMessage}
|
||||
selection={selection}
|
||||
search={search}
|
||||
sorting={sorting}
|
||||
sorting={true}
|
||||
hasActions={!this.state.hideWriteControls}
|
||||
data-test-subj="itemsInMemTable"
|
||||
/>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,6 +18,6 @@
|
|||
*/
|
||||
|
||||
import dateMath from '@elastic/datemath';
|
||||
export const GTE_INTERVAL_RE = new RegExp(`^>=([\\d\\.]*\\s*(${dateMath.units.join('|')}))$`);
|
||||
export const INTERVAL_STRING_RE = new RegExp('^([0-9\\.]*)\\s*(' + dateMath.units.join('|') + ')$');
|
||||
export const GTE_INTERVAL_RE = new RegExp(`^>=([\\d\\.]+\\s*(${dateMath.units.join('|')}))$`);
|
||||
export const INTERVAL_STRING_RE = new RegExp(`^([\\d\\.]+)\\s*(${dateMath.units.join('|')})$`);
|
||||
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 { GTE_INTERVAL_RE, INTERVAL_STRING_RE } from './interval_regexp';
|
||||
|
||||
describe('interval_regexp', () => {
|
||||
describe('GTE_INTERVAL_RE', () => {
|
||||
test('returns true for">=12h"', () => {
|
||||
const value = GTE_INTERVAL_RE.test('>=12h');
|
||||
|
||||
expect(value).toBeTruthy();
|
||||
});
|
||||
test('returns true for ">=1y"', () => {
|
||||
const value = GTE_INTERVAL_RE.test('>=12h');
|
||||
|
||||
expect(value).toBeTruthy();
|
||||
});
|
||||
test('returns true for ">=25m"', () => {
|
||||
const value = GTE_INTERVAL_RE.test('>=12h');
|
||||
|
||||
expect(value).toBeTruthy();
|
||||
});
|
||||
test('returns false "auto"', () => {
|
||||
const value = GTE_INTERVAL_RE.test('auto');
|
||||
|
||||
expect(value).toBeFalsy();
|
||||
});
|
||||
test('returns false "wrongInput"', () => {
|
||||
const value = GTE_INTERVAL_RE.test('wrongInput');
|
||||
|
||||
expect(value).toBeFalsy();
|
||||
});
|
||||
test('returns false "d"', () => {
|
||||
const value = GTE_INTERVAL_RE.test('d');
|
||||
|
||||
expect(value).toBeFalsy();
|
||||
});
|
||||
|
||||
test('returns false "y"', () => {
|
||||
const value = GTE_INTERVAL_RE.test('y');
|
||||
|
||||
expect(value).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('INTERVAL_STRING_RE', () => {
|
||||
test('returns true for "8d"', () => {
|
||||
const value = INTERVAL_STRING_RE.test('8d');
|
||||
|
||||
expect(value).toBeTruthy();
|
||||
});
|
||||
test('returns true for "1y"', () => {
|
||||
const value = INTERVAL_STRING_RE.test('1y');
|
||||
|
||||
expect(value).toBeTruthy();
|
||||
});
|
||||
test('returns true for "6M"', () => {
|
||||
const value = INTERVAL_STRING_RE.test('6M');
|
||||
|
||||
expect(value).toBeTruthy();
|
||||
});
|
||||
test('returns false "auto"', () => {
|
||||
const value = INTERVAL_STRING_RE.test('auto');
|
||||
|
||||
expect(value).toBeFalsy();
|
||||
});
|
||||
test('returns false "wrongInput"', () => {
|
||||
const value = INTERVAL_STRING_RE.test('wrongInput');
|
||||
|
||||
expect(value).toBeFalsy();
|
||||
});
|
||||
test('returns false for">=21h"', () => {
|
||||
const value = INTERVAL_STRING_RE.test('>=21h');
|
||||
|
||||
expect(value).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -64,10 +64,12 @@ class PercentilesUi extends Component {
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiFieldNumber
|
||||
aria-label={intl.formatMessage({ id: 'tsvb.percentile.percentileAriaLabel', defaultMessage: 'Percentile' })}
|
||||
placeholder={intl.formatMessage({ id: 'tsvb.percentile.percentilePlaceholder', defaultMessage: 'Percentile' })}
|
||||
placeholder={0}
|
||||
max={100}
|
||||
min={0}
|
||||
step={1}
|
||||
onChange={this.handleTextChange(model, 'value')}
|
||||
value={Number(model.value)}
|
||||
value={model.value === '' ? '' : Number(model.value)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
|
|
@ -117,6 +117,7 @@ export const IndexPattern = props => {
|
|||
disabled={props.disabled}
|
||||
onChange={handleTextChange(intervalName, 'auto')}
|
||||
value={model[intervalName]}
|
||||
placeholder={'auto'}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -16,32 +16,20 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { relativeOptions } from '../../../../../ui/public/timepicker/relative_options';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { convertIntervalIntoUnit } from './get_interval';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const unitLookup = {
|
||||
s: i18n.translate('tsvb.axisLabelOptions.secondsLabel', { defaultMessage: 'seconds' }),
|
||||
m: i18n.translate('tsvb.axisLabelOptions.minutesLabel', { defaultMessage: 'minutes' }),
|
||||
h: i18n.translate('tsvb.axisLabelOptions.hoursLabel', { defaultMessage: 'hours' }),
|
||||
d: i18n.translate('tsvb.axisLabelOptions.daysLabel', { defaultMessage: 'days' }),
|
||||
w: i18n.translate('tsvb.axisLabelOptions.weeksLabel', { defaultMessage: 'weeks' }),
|
||||
M: i18n.translate('tsvb.axisLabelOptions.monthsLabel', { defaultMessage: 'months' }),
|
||||
y: i18n.translate('tsvb.axisLabelOptions.yearsLabel', { defaultMessage: 'years' })
|
||||
};
|
||||
export function getAxisLabelString(interval) {
|
||||
const units = _.pluck(_.clone(relativeOptions).reverse(), 'value')
|
||||
.filter(s => /^[smhdwMy]$/.test(s));
|
||||
const duration = moment.duration(interval, 'ms');
|
||||
for (let i = 0; i < units.length; i++) {
|
||||
const as = duration.as(units[i]);
|
||||
if (Math.abs(as) > 1) {
|
||||
const unitValue = Math.round(Math.abs(as));
|
||||
const unitString = unitLookup[units[i]];
|
||||
return i18n.translate('tsvb.axisLabelOptions.axisLabel',
|
||||
{ defaultMessage: 'per {unitValue} {unitString}', values: { unitValue, unitString } });
|
||||
}
|
||||
const convertedValue = convertIntervalIntoUnit(interval);
|
||||
|
||||
if (convertedValue) {
|
||||
return i18n.translate('tsvb.axisLabelOptions.axisLabel',
|
||||
{
|
||||
defaultMessage: 'per {unitValue} {unitString}',
|
||||
values: {
|
||||
unitValue: convertedValue.unitValue,
|
||||
unitString: convertedValue.unitString,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 moment from 'moment';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { pluck, get, clone, isString } from 'lodash';
|
||||
import { relativeOptions } from '../../../../../ui/public/timepicker/relative_options';
|
||||
|
||||
import { GTE_INTERVAL_RE, INTERVAL_STRING_RE } from '../../../common/interval_regexp';
|
||||
|
||||
export const unitLookup = {
|
||||
s: i18n.translate('tsvb.getInterval.secondsLabel', { defaultMessage: 'seconds' }),
|
||||
m: i18n.translate('tsvb.getInterval.minutesLabel', { defaultMessage: 'minutes' }),
|
||||
h: i18n.translate('tsvb.getInterval.hoursLabel', { defaultMessage: 'hours' }),
|
||||
d: i18n.translate('tsvb.getInterval.daysLabel', { defaultMessage: 'days' }),
|
||||
w: i18n.translate('tsvb.getInterval.weeksLabel', { defaultMessage: 'weeks' }),
|
||||
M: i18n.translate('tsvb.getInterval.monthsLabel', { defaultMessage: 'months' }),
|
||||
y: i18n.translate('tsvb.getInterval.yearsLabel', { defaultMessage: 'years' })
|
||||
};
|
||||
|
||||
export const convertIntervalIntoUnit = (interval, hasTranslateUnitString = true) => {
|
||||
const units = pluck(clone(relativeOptions).reverse(), 'value')
|
||||
.filter(s => /^[smhdwMy]$/.test(s));
|
||||
const duration = moment.duration(interval, 'ms');
|
||||
|
||||
for (let i = 0; i < units.length; i++) {
|
||||
const as = duration.as(units[i]);
|
||||
|
||||
if (Math.abs(as) > 1) {
|
||||
return {
|
||||
unitValue: Math.round(Math.abs(as)),
|
||||
unitString: hasTranslateUnitString ? unitLookup[units[i]] : units[i]
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
export const isGteInterval = (interval) => GTE_INTERVAL_RE.test(interval);
|
||||
|
||||
export const isIntervalValid = (interval) => {
|
||||
return isString(interval) &&
|
||||
(interval === 'auto' || INTERVAL_STRING_RE.test(interval) || isGteInterval(interval));
|
||||
};
|
||||
|
||||
export const getInterval = (visData, model) => {
|
||||
let series;
|
||||
|
||||
if (model && model.type === 'table') {
|
||||
series = get(visData, `series[0].series`, []);
|
||||
} else {
|
||||
series = get(visData, `${model.id}.series`, []);
|
||||
}
|
||||
|
||||
return series.reduce((currentInterval, item) => {
|
||||
if (item.data.length > 1) {
|
||||
const seriesInterval = item.data[1][0] - item.data[0][0];
|
||||
if (!currentInterval || seriesInterval < currentInterval) return seriesInterval;
|
||||
}
|
||||
return currentInterval;
|
||||
}, 0);
|
||||
};
|
|
@ -23,7 +23,7 @@ import FieldSelect from '../aggs/field_select';
|
|||
import SeriesEditor from '../series_editor';
|
||||
import { IndexPattern } from '../index_pattern';
|
||||
import createTextHandler from '../lib/create_text_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import { get } from 'lodash';
|
||||
import uuid from 'uuid';
|
||||
import YesNo from '../yes_no';
|
||||
import {
|
||||
|
@ -64,11 +64,21 @@ class TablePanelConfig extends Component {
|
|||
this.setState({ selectedTab });
|
||||
}
|
||||
|
||||
handlePivotChange = (selectedOption) => {
|
||||
const { fields, model } = this.props;
|
||||
const pivotId = get(selectedOption, '[0].value', null);
|
||||
const field = fields[model.index_pattern].find(field => field.name === pivotId);
|
||||
const pivotType = field.type || model.pivot_type;
|
||||
this.props.onChange({
|
||||
pivot_id: pivotId,
|
||||
pivot_type: pivotType
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { selectedTab } = this.state;
|
||||
const defaults = { drilldown_url: '', filter: '', pivot_label: '', pivot_rows: 10 };
|
||||
const defaults = { drilldown_url: '', filter: '', pivot_label: '', pivot_rows: 10, pivot_type: '' };
|
||||
const model = { ...defaults, ...this.props.model };
|
||||
const handleSelectChange = createSelectHandler(this.props.onChange);
|
||||
const handleTextChange = createTextHandler(this.props.onChange);
|
||||
const htmlId = htmlIdGenerator();
|
||||
let view;
|
||||
|
@ -100,7 +110,7 @@ class TablePanelConfig extends Component {
|
|||
fields={this.props.fields}
|
||||
value={model.pivot_id}
|
||||
indexPattern={model.index_pattern}
|
||||
onChange={handleSelectChange('pivot_id')}
|
||||
onChange={this.handlePivotChange}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
|
|
@ -1,196 +1,173 @@
|
|||
/*
|
||||
* 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 PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import * as Rx from 'rxjs';
|
||||
import { share } from 'rxjs/operators';
|
||||
import VisEditorVisualization from './vis_editor_visualization';
|
||||
import Visualization from './visualization';
|
||||
import VisPicker from './vis_picker';
|
||||
import PanelConfig from './panel_config';
|
||||
import brushHandler from '../lib/create_brush_handler';
|
||||
import { extractIndexPatterns } from '../lib/extract_index_patterns';
|
||||
import { fetchFields } from '../lib/fetch_fields';
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
class VisEditor extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { vis } = props;
|
||||
this.appState = vis.API.getAppState();
|
||||
this.state = {
|
||||
model: props.visParams,
|
||||
dirty: false,
|
||||
autoApply: true,
|
||||
visFields: {},
|
||||
};
|
||||
this.onBrush = brushHandler(props.vis.API.timeFilter);
|
||||
this.handleUiState = this.handleUiState.bind(this, props.vis);
|
||||
this.getConfig = this.getConfig.bind(this);
|
||||
this.visDataSubject = new Rx.Subject();
|
||||
this.visData$ = this.visDataSubject.asObservable().pipe(share());
|
||||
}
|
||||
|
||||
getConfig(...args) {
|
||||
return this.props.config.get(...args);
|
||||
}
|
||||
|
||||
handleUiState(vis, ...args) {
|
||||
vis.uiStateVal(...args);
|
||||
}
|
||||
|
||||
fetchIndexPatternFields = async () => {
|
||||
const { params } = this.props.vis;
|
||||
const { visFields } = this.state;
|
||||
const indexPatterns = extractIndexPatterns(params, visFields);
|
||||
const fields = await fetchFields(indexPatterns);
|
||||
this.setState((previousState) => {
|
||||
return {
|
||||
visFields: {
|
||||
...previousState.visFields,
|
||||
...fields,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
setDefaultIndexPattern = async () => {
|
||||
const savedObjectsClient = chrome.getSavedObjectsClient();
|
||||
const indexPattern = await savedObjectsClient.get('index-pattern', this.getConfig('defaultIndex'));
|
||||
|
||||
this.handleChange({
|
||||
default_index_pattern: indexPattern.attributes.title
|
||||
});
|
||||
}
|
||||
|
||||
handleChange = async (partialModel) => {
|
||||
const nextModel = { ...this.state.model, ...partialModel };
|
||||
this.props.vis.params = nextModel;
|
||||
if (this.state.autoApply) {
|
||||
this.props.vis.updateState();
|
||||
}
|
||||
this.setState({
|
||||
model: nextModel,
|
||||
dirty: !this.state.autoApply,
|
||||
});
|
||||
this.fetchIndexPatternFields();
|
||||
}
|
||||
|
||||
handleCommit = () => {
|
||||
this.props.vis.updateState();
|
||||
this.setState({ dirty: false });
|
||||
}
|
||||
|
||||
handleAutoApplyToggle = (event) => {
|
||||
this.setState({ autoApply: event.target.checked });
|
||||
}
|
||||
|
||||
onDataChange = ({ visData }) => {
|
||||
this.visDataSubject.next(visData);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.isEditorMode) {
|
||||
if (!this.props.visParams || !this.props.visData) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Visualization
|
||||
dateFormat={this.props.config.get('dateFormat')}
|
||||
onBrush={this.onBrush}
|
||||
onUiState={this.handleUiState}
|
||||
uiState={this.props.vis.getUiState()}
|
||||
fields={this.state.visFields}
|
||||
model={this.props.visParams}
|
||||
visData={this.props.visData}
|
||||
getConfig={this.getConfig}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { model } = this.state;
|
||||
|
||||
if (model) {
|
||||
return (
|
||||
<div className="tvbEditor">
|
||||
<div className="tvbEditor--hideForReporting">
|
||||
<VisPicker model={model} onChange={this.handleChange} />
|
||||
</div>
|
||||
<VisEditorVisualization
|
||||
dirty={this.state.dirty}
|
||||
autoApply={this.state.autoApply}
|
||||
model={model}
|
||||
appState={this.appState}
|
||||
savedObj={this.props.savedObj}
|
||||
timeRange={this.props.timeRange}
|
||||
onUiState={this.handleUiState}
|
||||
uiState={this.props.vis.getUiState()}
|
||||
onBrush={this.onBrush}
|
||||
onCommit={this.handleCommit}
|
||||
onToggleAutoApply={this.handleAutoApplyToggle}
|
||||
onChange={this.handleChange}
|
||||
title={this.props.vis.title}
|
||||
description={this.props.vis.description}
|
||||
dateFormat={this.props.config.get('dateFormat')}
|
||||
onDataChange={this.onDataChange}
|
||||
/>
|
||||
<div className="tvbEditor--hideForReporting">
|
||||
<PanelConfig
|
||||
fields={this.state.visFields}
|
||||
model={model}
|
||||
visData$={this.visData$}
|
||||
dateFormat={this.props.config.get('dateFormat')}
|
||||
onChange={this.handleChange}
|
||||
getConfig={this.getConfig}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await this.setDefaultIndexPattern();
|
||||
await this.fetchIndexPatternFields();
|
||||
this.props.renderComplete();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.props.renderComplete();
|
||||
}
|
||||
}
|
||||
|
||||
VisEditor.defaultProps = {
|
||||
visData: {}
|
||||
};
|
||||
|
||||
VisEditor.propTypes = {
|
||||
vis: PropTypes.object,
|
||||
visData: PropTypes.object,
|
||||
renderComplete: PropTypes.func,
|
||||
config: PropTypes.object,
|
||||
isEditorMode: PropTypes.bool,
|
||||
savedObj: PropTypes.object,
|
||||
timeRange: PropTypes.object,
|
||||
};
|
||||
|
||||
export default VisEditor;
|
||||
/*
|
||||
* 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 PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import * as Rx from 'rxjs';
|
||||
import { share } from 'rxjs/operators';
|
||||
import VisEditorVisualization from './vis_editor_visualization';
|
||||
import Visualization from './visualization';
|
||||
import VisPicker from './vis_picker';
|
||||
import PanelConfig from './panel_config';
|
||||
import brushHandler from '../lib/create_brush_handler';
|
||||
import { fetchIndexPatternFields } from '../lib/fetch_fields';
|
||||
|
||||
class VisEditor extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { vis } = props;
|
||||
this.appState = vis.API.getAppState();
|
||||
this.state = {
|
||||
model: props.visParams,
|
||||
dirty: false,
|
||||
autoApply: true,
|
||||
visFields: props.visFields
|
||||
};
|
||||
this.onBrush = brushHandler(props.vis.API.timeFilter);
|
||||
this.visDataSubject = new Rx.Subject();
|
||||
this.visData$ = this.visDataSubject.asObservable().pipe(share());
|
||||
}
|
||||
|
||||
get uiState() {
|
||||
return this.props.vis.getUiState();
|
||||
}
|
||||
|
||||
getConfig = (...args) => {
|
||||
return this.props.config.get(...args);
|
||||
};
|
||||
|
||||
handleUiState = (field, value) => {
|
||||
this.props.vis.uiStateVal(field, value);
|
||||
};
|
||||
|
||||
handleChange = async (partialModel) => {
|
||||
const nextModel = { ...this.state.model, ...partialModel };
|
||||
this.props.vis.params = nextModel;
|
||||
if (this.state.autoApply) {
|
||||
this.props.vis.updateState();
|
||||
}
|
||||
this.setState({
|
||||
model: nextModel,
|
||||
dirty: !this.state.autoApply
|
||||
});
|
||||
const { params, fields } = this.props.vis;
|
||||
fetchIndexPatternFields(params, fields).then(visFields => {
|
||||
this.setState({ visFields });
|
||||
});
|
||||
};
|
||||
|
||||
handleCommit = () => {
|
||||
this.props.vis.updateState();
|
||||
this.setState({ dirty: false });
|
||||
};
|
||||
|
||||
handleAutoApplyToggle = (event) => {
|
||||
this.setState({ autoApply: event.target.checked });
|
||||
};
|
||||
|
||||
onDataChange = ({ visData }) => {
|
||||
this.visDataSubject.next(visData);
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.props.isEditorMode) {
|
||||
if (!this.props.visParams || !this.props.visData) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Visualization
|
||||
dateFormat={this.props.config.get('dateFormat')}
|
||||
onBrush={this.onBrush}
|
||||
onUiState={this.handleUiState}
|
||||
uiState={this.uiState}
|
||||
model={this.props.visParams}
|
||||
visData={this.props.visData}
|
||||
getConfig={this.getConfig}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { model } = this.state;
|
||||
|
||||
if (model) {
|
||||
return (
|
||||
<div className="tvbEditor">
|
||||
<div className="tvbEditor--hideForReporting">
|
||||
<VisPicker model={model} onChange={this.handleChange} />
|
||||
</div>
|
||||
<VisEditorVisualization
|
||||
dirty={this.state.dirty}
|
||||
autoApply={this.state.autoApply}
|
||||
model={model}
|
||||
appState={this.appState}
|
||||
savedObj={this.props.savedObj}
|
||||
timeRange={this.props.timeRange}
|
||||
onUiState={this.handleUiState}
|
||||
uiState={this.uiState}
|
||||
onBrush={this.onBrush}
|
||||
onCommit={this.handleCommit}
|
||||
onToggleAutoApply={this.handleAutoApplyToggle}
|
||||
onChange={this.handleChange}
|
||||
title={this.props.vis.title}
|
||||
description={this.props.vis.description}
|
||||
dateFormat={this.props.config.get('dateFormat')}
|
||||
onDataChange={this.onDataChange}
|
||||
/>
|
||||
<div className="tvbEditor--hideForReporting">
|
||||
<PanelConfig
|
||||
fields={this.state.visFields}
|
||||
model={model}
|
||||
visData$={this.visData$}
|
||||
dateFormat={this.props.config.get('dateFormat')}
|
||||
onChange={this.handleChange}
|
||||
getConfig={this.getConfig}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.renderComplete();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.props.renderComplete();
|
||||
}
|
||||
}
|
||||
|
||||
VisEditor.defaultProps = {
|
||||
visData: {}
|
||||
};
|
||||
|
||||
VisEditor.propTypes = {
|
||||
vis: PropTypes.object,
|
||||
visData: PropTypes.object,
|
||||
visFields: PropTypes.object,
|
||||
renderComplete: PropTypes.func,
|
||||
config: PropTypes.object,
|
||||
isEditorMode: PropTypes.bool,
|
||||
savedObj: PropTypes.object,
|
||||
timeRange: PropTypes.object,
|
||||
};
|
||||
|
||||
export default VisEditor;
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { get } from 'lodash';
|
||||
import { keyCodes, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui';
|
||||
import { getVisualizeLoader } from 'ui/visualize/loader/visualize_loader';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import { getInterval, convertIntervalIntoUnit, isIntervalValid, isGteInterval } from './lib/get_interval';
|
||||
|
||||
const MIN_CHART_HEIGHT = 250;
|
||||
|
||||
|
@ -30,7 +31,8 @@ class VisEditorVisualization extends Component {
|
|||
super(props);
|
||||
this.state = {
|
||||
height: MIN_CHART_HEIGHT,
|
||||
dragging: false
|
||||
dragging: false,
|
||||
panelInterval: 0,
|
||||
};
|
||||
|
||||
this.handleMouseUp = this.handleMouseUp.bind(this);
|
||||
|
@ -53,7 +55,7 @@ class VisEditorVisualization extends Component {
|
|||
this.handleMouseMove = (event) => {
|
||||
if (this.state.dragging) {
|
||||
this.setState((prevState) => ({
|
||||
height: Math.max(MIN_CHART_HEIGHT, prevState.height + event.movementY)
|
||||
height: Math.max(MIN_CHART_HEIGHT, prevState.height + event.movementY),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
@ -67,16 +69,16 @@ class VisEditorVisualization extends Component {
|
|||
if (this._handler) {
|
||||
this._handler.destroy();
|
||||
}
|
||||
if(this._subscription) {
|
||||
if (this._subscription) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
onUpdate = () => {
|
||||
this._handler.update({
|
||||
timeRange: this.props.timeRange
|
||||
timeRange: this.props.timeRange,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_loadVisualization() {
|
||||
getVisualizeLoader().then(loader => {
|
||||
|
@ -94,6 +96,7 @@ class VisEditorVisualization extends Component {
|
|||
});
|
||||
|
||||
this._subscription = this._handler.data$.subscribe((data) => {
|
||||
this.setPanelInterval(data.visData);
|
||||
this.props.onDataChange(data);
|
||||
});
|
||||
|
||||
|
@ -103,6 +106,14 @@ class VisEditorVisualization extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
setPanelInterval(visData) {
|
||||
const panelInterval = getInterval(visData, this.props.model);
|
||||
|
||||
if (this.state.panelInterval !== panelInterval) {
|
||||
this.setState({ panelInterval });
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (!this._handler) {
|
||||
this._handlerUpdateHasAlreadyBeenTriggered = true;
|
||||
|
@ -115,6 +126,7 @@ class VisEditorVisualization extends Component {
|
|||
componentDidMount() {
|
||||
this._loadVisualization();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the chart height when pressing up/down while the drag handle
|
||||
* for resizing has the focus.
|
||||
|
@ -128,12 +140,40 @@ class VisEditorVisualization extends Component {
|
|||
this.setState((prevState) => {
|
||||
const newHeight = prevState.height + (keyCode === keyCodes.UP ? -15 : 15);
|
||||
return {
|
||||
height: Math.max(MIN_CHART_HEIGHT, newHeight)
|
||||
height: Math.max(MIN_CHART_HEIGHT, newHeight),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
hasShowPanelIntervalValue() {
|
||||
const type = get(this.props, 'model.type', '');
|
||||
|
||||
return [
|
||||
'metric',
|
||||
'top_n',
|
||||
'gauge',
|
||||
'markdown',
|
||||
'table',
|
||||
].includes(type);
|
||||
}
|
||||
|
||||
getFormattedPanelInterval() {
|
||||
const interval = get(this.props, 'model.interval') || 'auto';
|
||||
const isValid = isIntervalValid(interval);
|
||||
const shouldShowActualInterval = interval === 'auto' || isGteInterval(interval);
|
||||
|
||||
if (shouldShowActualInterval || !isValid) {
|
||||
const autoInterval = convertIntervalIntoUnit(this.state.panelInterval, false);
|
||||
|
||||
if (autoInterval) {
|
||||
return `${autoInterval.unitValue}${autoInterval.unitString}`;
|
||||
}
|
||||
} else {
|
||||
return interval;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dirty, autoApply } = this.props;
|
||||
const style = { height: this.state.height };
|
||||
|
@ -141,6 +181,8 @@ class VisEditorVisualization extends Component {
|
|||
style.userSelect = 'none';
|
||||
}
|
||||
|
||||
const panelInterval = this.hasShowPanelIntervalValue() && this.getFormattedPanelInterval();
|
||||
|
||||
let applyMessage = (<FormattedMessage
|
||||
id="tsvb.visEditorVisualization.changesSuccessfullyAppliedMessage"
|
||||
defaultMessage="The latest changes have been applied."
|
||||
|
@ -171,6 +213,22 @@ class VisEditorVisualization extends Component {
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
{panelInterval &&
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="default" size="xs">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="tsvb.visEditorVisualization.panelInterval"
|
||||
defaultMessage="Interval: {panelInterval}"
|
||||
values={{
|
||||
panelInterval: panelInterval,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
}
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color={dirty ? 'default' : 'subdued'} size="xs">
|
||||
<p>
|
||||
|
@ -180,14 +238,14 @@ class VisEditorVisualization extends Component {
|
|||
</EuiFlexItem>
|
||||
|
||||
{!autoApply &&
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton iconType="play" fill size="s" onClick={this.props.onCommit} disabled={!dirty}>
|
||||
<FormattedMessage
|
||||
id="tsvb.visEditorVisualization.applyChangesLabel"
|
||||
defaultMessage="Apply changes"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton iconType="play" fill size="s" onClick={this.props.onCommit} disabled={!dirty}>
|
||||
<FormattedMessage
|
||||
id="tsvb.visEditorVisualization.applyChangesLabel"
|
||||
defaultMessage="Apply changes"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
@ -213,10 +271,10 @@ class VisEditorVisualization extends Component {
|
|||
onKeyDown={this.onSizeHandleKeyDown}
|
||||
aria-label={this.props.intl.formatMessage({
|
||||
id: 'tsvb.colorRules.adjustChartSizeAriaLabel',
|
||||
defaultMessage: 'Press up/down to adjust the chart size'
|
||||
defaultMessage: 'Press up/down to adjust the chart size',
|
||||
})}
|
||||
>
|
||||
<i className="fa fa-ellipsis-h" />
|
||||
<i className="fa fa-ellipsis-h"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import _ from 'lodash';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { fieldFormats } from 'ui/registry/field_formats';
|
||||
import tickFormatter from '../../lib/tick_formatter';
|
||||
import calculateLabel from '../../../../common/calculate_label';
|
||||
import { isSortable } from './is_sortable';
|
||||
|
@ -27,6 +28,8 @@ import { EuiToolTip, EuiIcon } from '@elastic/eui';
|
|||
import replaceVars from '../../lib/replace_vars';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
const DateFormat = fieldFormats.getType('date');
|
||||
|
||||
function getColor(rules, colorKey, value) {
|
||||
let color;
|
||||
if (rules) {
|
||||
|
@ -51,13 +54,12 @@ class TableVis extends Component {
|
|||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.renderRow = this.renderRow.bind(this);
|
||||
this.dateFormatter = new DateFormat({}, this.props.getConfig);
|
||||
}
|
||||
|
||||
renderRow(row) {
|
||||
renderRow = row => {
|
||||
const { model } = this.props;
|
||||
const rowId = row.key;
|
||||
let rowDisplay = rowId;
|
||||
let rowDisplay = model.pivot_type === 'date' ? this.dateFormatter.convert(row.key) : row.key;
|
||||
if (model.drilldown_url) {
|
||||
const url = replaceVars(model.drilldown_url, {}, { key: row.key });
|
||||
rowDisplay = (<a href={url}>{rowDisplay}</a>);
|
||||
|
@ -78,14 +80,14 @@ class TableVis extends Component {
|
|||
}
|
||||
const style = { color: getColor(column.color_rules, 'text', item.last) };
|
||||
return (
|
||||
<td key={`${rowId}-${item.id}`} data-test-subj="tvbTableVis__value" className="eui-textRight" style={style}>
|
||||
<td key={`${row.key}-${item.id}`} data-test-subj="tvbTableVis__value" className="eui-textRight" style={style}>
|
||||
<span>{ value }</span>
|
||||
{trend}
|
||||
</td>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<tr key={rowId}>
|
||||
<tr key={row.key}>
|
||||
<td>{rowDisplay}</td>
|
||||
{columns}
|
||||
</tr>
|
||||
|
|
|
@ -27,6 +27,7 @@ import _ from 'lodash';
|
|||
import Timeseries from '../../../visualizations/components/timeseries';
|
||||
import replaceVars from '../../lib/replace_vars';
|
||||
import { getAxisLabelString } from '../../lib/get_axis_label_string';
|
||||
import { getInterval } from '../../lib/get_interval';
|
||||
import { createXaxisFormatter } from '../../lib/create_xaxis_formatter';
|
||||
|
||||
function hasSeparateAxis(row) {
|
||||
|
@ -35,20 +36,10 @@ function hasSeparateAxis(row) {
|
|||
|
||||
class TimeseriesVisualization extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
getInterval = () => {
|
||||
const { visData, model } = this.props;
|
||||
const series = _.get(visData, `${model.id}.series`, []);
|
||||
return series.reduce((currentInterval, item) => {
|
||||
if (item.data.length > 1) {
|
||||
const seriesInterval = item.data[1][0] - item.data[0][0];
|
||||
if (!currentInterval || seriesInterval < currentInterval) return seriesInterval;
|
||||
}
|
||||
return currentInterval;
|
||||
}, 0);
|
||||
|
||||
return getInterval(visData, model);
|
||||
}
|
||||
|
||||
xaxisFormatter = (val) => {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue