diff --git a/.backportrc.json b/.backportrc.json index 0894909d2aac4..87bc3a1be583b 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -25,7 +25,7 @@ ], "targetPRLabels": ["backport"], "branchLabelMapping": { - "^v7.8.0$": "7.x", + "^v7.9.0$": "7.x", "^v(\\d+).(\\d+).\\d+$": "$1.$2" } } diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md new file mode 100644 index 0000000000000..7536cd2b07ae6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsMigrationLogger](./kibana-plugin-core-server.savedobjectsmigrationlogger.md) > [error](./kibana-plugin-core-server.savedobjectsmigrationlogger.error.md) + +## SavedObjectsMigrationLogger.error property + +Signature: + +```typescript +error: (msg: string, meta: LogMeta) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md index 066643516b213..1b691ee8cb16d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md @@ -16,6 +16,7 @@ export interface SavedObjectsMigrationLogger | Property | Type | Description | | --- | --- | --- | | [debug](./kibana-plugin-core-server.savedobjectsmigrationlogger.debug.md) | (msg: string) => void | | +| [error](./kibana-plugin-core-server.savedobjectsmigrationlogger.error.md) | (msg: string, meta: LogMeta) => void | | | [info](./kibana-plugin-core-server.savedobjectsmigrationlogger.info.md) | (msg: string) => void | | | [warn](./kibana-plugin-core-server.savedobjectsmigrationlogger.warn.md) | (msg: string) => void | | | [warning](./kibana-plugin-core-server.savedobjectsmigrationlogger.warning.md) | (msg: string) => void | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md new file mode 100644 index 0000000000000..a4d6abcf86a94 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [getTimeField](./kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md) + +## IIndexPattern.getTimeField() method + +Signature: + +```typescript +getTimeField?(): IFieldType | undefined; +``` +Returns: + +`IFieldType | undefined` + diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts index 3ec478e3ca28d..bd10520ca1c57 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts @@ -293,7 +293,7 @@ describe('DocumentMigrator', () => { migrationVersion: { dog: '10.2.0' }, }) ).toThrow( - /Document "smelly" has property "dog" which belongs to a more recent version of Kibana \(10\.2\.0\)/i + /Document "smelly" has property "dog" which belongs to a more recent version of Kibana \[10\.2\.0\]\. The last known version is \[undefined\]/i ); }); @@ -315,7 +315,7 @@ describe('DocumentMigrator', () => { migrationVersion: { dawg: '1.2.4' }, }) ).toThrow( - /Document "fleabag" has property "dawg" which belongs to a more recent version of Kibana \(1\.2\.4\)/i + /Document "fleabag" has property "dawg" which belongs to a more recent version of Kibana \[1\.2\.4\]\. The last known version is \[1\.2\.3\]/i ); }); diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts index 4ddb2b070d3ac..07c1da5586107 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts @@ -350,7 +350,7 @@ function nextUnmigratedProp(doc: SavedObjectUnsanitizedDoc, migrations: ActiveMi if (docVersion && (!latestVersion || Semver.gt(docVersion, latestVersion))) { throw Boom.badData( `Document "${doc.id}" has property "${p}" which belongs to a more recent` + - ` version of Kibana (${docVersion}).`, + ` version of Kibana [${docVersion}]. The last known version is [${latestVersion}]`, doc ); } diff --git a/src/core/server/saved_objects/migrations/core/index_migrator.ts b/src/core/server/saved_objects/migrations/core/index_migrator.ts index c75fa68572c71..ef2a8870d78d0 100644 --- a/src/core/server/saved_objects/migrations/core/index_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/index_migrator.ts @@ -195,7 +195,7 @@ async function migrateSourceToDest(context: Context) { await Index.write( callCluster, dest.indexName, - migrateRawDocs(serializer, documentMigrator.migrate, docs) + migrateRawDocs(serializer, documentMigrator.migrate, docs, log) ); } } diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts index 89f3fde384848..e55b72be2436d 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts @@ -21,6 +21,7 @@ import _ from 'lodash'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { SavedObjectsSerializer } from '../../serialization'; import { migrateRawDocs } from './migrate_raw_docs'; +import { createSavedObjectsMigrationLoggerMock } from '../../migrations/mocks'; describe('migrateRawDocs', () => { test('converts raw docs to saved objects', async () => { @@ -31,7 +32,8 @@ describe('migrateRawDocs', () => { [ { _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }, { _id: 'c:d', _source: { type: 'c', c: { name: 'DDD' } } }, - ] + ], + createSavedObjectsMigrationLoggerMock() ); expect(result).toEqual([ @@ -48,7 +50,8 @@ describe('migrateRawDocs', () => { expect(transform).toHaveBeenCalled(); }); - test('passes invalid docs through untouched', async () => { + test('passes invalid docs through untouched and logs error', async () => { + const logger = createSavedObjectsMigrationLoggerMock(); const transform = jest.fn((doc: any) => _.set(_.cloneDeep(doc), 'attributes.name', 'TADA') ); @@ -58,7 +61,8 @@ describe('migrateRawDocs', () => { [ { _id: 'foo:b', _source: { type: 'a', a: { name: 'AAA' } } }, { _id: 'c:d', _source: { type: 'c', c: { name: 'DDD' } } }, - ] + ], + logger ); expect(result).toEqual([ @@ -82,5 +86,7 @@ describe('migrateRawDocs', () => { }, ], ]); + + expect(logger.error).toBeCalledTimes(1); }); }); diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts index 5fe15f40db8ec..49acea82e1c8a 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts @@ -23,6 +23,7 @@ import { SavedObjectsRawDoc, SavedObjectsSerializer } from '../../serialization'; import { TransformFn } from './document_migrator'; +import { SavedObjectsMigrationLogger } from '.'; /** * Applies the specified migration function to every saved object document in the list @@ -35,7 +36,8 @@ import { TransformFn } from './document_migrator'; export function migrateRawDocs( serializer: SavedObjectsSerializer, migrateDoc: TransformFn, - rawDocs: SavedObjectsRawDoc[] + rawDocs: SavedObjectsRawDoc[], + log: SavedObjectsMigrationLogger ): SavedObjectsRawDoc[] { return rawDocs.map(raw => { if (serializer.isRawSavedObject(raw)) { @@ -47,6 +49,10 @@ export function migrateRawDocs( }); } + log.error( + `Error: Unable to migrate the corrupt Saved Object document ${raw._id}. To prevent Kibana from performing a migration on every restart, please delete or fix this document by ensuring that the namespace and type in the document's id matches the values in the namespace and type fields.`, + { rawDocument: raw } + ); return raw; }); } diff --git a/src/core/server/saved_objects/migrations/core/migration_coordinator.test.ts b/src/core/server/saved_objects/migrations/core/migration_coordinator.test.ts index 800edaeaa5885..3f2c31a7c0e5c 100644 --- a/src/core/server/saved_objects/migrations/core/migration_coordinator.test.ts +++ b/src/core/server/saved_objects/migrations/core/migration_coordinator.test.ts @@ -19,14 +19,10 @@ import _ from 'lodash'; import { coordinateMigration } from './migration_coordinator'; +import { createSavedObjectsMigrationLoggerMock } from '../mocks'; describe('coordinateMigration', () => { - const log = { - debug: jest.fn(), - warning: jest.fn(), - warn: jest.fn(), - info: jest.fn(), - }; + const log = createSavedObjectsMigrationLoggerMock(); test('waits for isMigrated, if there is an index conflict', async () => { const pollInterval = 1; diff --git a/src/core/server/saved_objects/migrations/core/migration_logger.ts b/src/core/server/saved_objects/migrations/core/migration_logger.ts index 9dfb3abc8e72d..00ed8bf0b73fc 100644 --- a/src/core/server/saved_objects/migrations/core/migration_logger.ts +++ b/src/core/server/saved_objects/migrations/core/migration_logger.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Logger } from 'src/core/server/logging'; +import { Logger, LogMeta } from '../../../logging'; /* * This file provides a helper class for ensuring that all logging @@ -35,6 +35,7 @@ export interface SavedObjectsMigrationLogger { */ warning: (msg: string) => void; warn: (msg: string) => void; + error: (msg: string, meta: LogMeta) => void; } export class MigrationLogger implements SavedObjectsMigrationLogger { @@ -48,4 +49,5 @@ export class MigrationLogger implements SavedObjectsMigrationLogger { public debug = (msg: string) => this.logger.debug(msg); public warning = (msg: string) => this.logger.warn(msg); public warn = (msg: string) => this.logger.warn(msg); + public error = (msg: string, meta: LogMeta) => this.logger.error(msg, meta); } diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts index dafd6c5341196..7d9ff9bed6d72 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts @@ -22,9 +22,9 @@ * (the shape of the mappings and documents in the index). */ -import { Logger } from 'src/core/server/logging'; import { KibanaConfigType } from 'src/core/server/kibana_config'; import { BehaviorSubject } from 'rxjs'; +import { Logger } from '../../../logging'; import { IndexMapping, SavedObjectsTypeMappingDefinitions } from '../../mappings'; import { SavedObjectUnsanitizedDoc, SavedObjectsSerializer } from '../../serialization'; import { docValidator, PropertyValidators } from '../../validation'; diff --git a/src/core/server/saved_objects/migrations/mocks.ts b/src/core/server/saved_objects/migrations/mocks.ts index 76a890d26bfa0..50a7191393472 100644 --- a/src/core/server/saved_objects/migrations/mocks.ts +++ b/src/core/server/saved_objects/migrations/mocks.ts @@ -20,12 +20,13 @@ import { SavedObjectMigrationContext } from './types'; import { SavedObjectsMigrationLogger } from './core'; -const createLoggerMock = (): jest.Mocked => { +export const createSavedObjectsMigrationLoggerMock = (): jest.Mocked => { const mock = { debug: jest.fn(), info: jest.fn(), warning: jest.fn(), warn: jest.fn(), + error: jest.fn(), }; return mock; @@ -33,7 +34,7 @@ const createLoggerMock = (): jest.Mocked => { const createContextMock = (): jest.Mocked => { const mock = { - log: createLoggerMock(), + log: createSavedObjectsMigrationLoggerMock(), }; return mock; }; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index bd6046b5ec281..e4234689c25e8 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -91,7 +91,6 @@ import { IngestGetPipelineParams } from 'elasticsearch'; import { IngestPutPipelineParams } from 'elasticsearch'; import { IngestSimulateParams } from 'elasticsearch'; import { KibanaConfigType } from 'src/core/server/kibana_config'; -import { Logger as Logger_2 } from 'src/core/server/logging'; import { MGetParams } from 'elasticsearch'; import { MGetResponse } from 'elasticsearch'; import { MSearchParams } from 'elasticsearch'; @@ -2169,6 +2168,8 @@ export interface SavedObjectsMigrationLogger { // (undocumented) debug: (msg: string) => void; // (undocumented) + error: (msg: string, meta: LogMeta) => void; + // (undocumented) info: (msg: string) => void; // (undocumented) warn: (msg: string) => void; diff --git a/src/legacy/ui/public/new_platform/new_platform.test.ts b/src/legacy/ui/public/new_platform/new_platform.test.ts index 1629aac588a61..21e7b559f71f5 100644 --- a/src/legacy/ui/public/new_platform/new_platform.test.ts +++ b/src/legacy/ui/public/new_platform/new_platform.test.ts @@ -22,6 +22,7 @@ jest.mock('history'); import { setRootControllerMock, historyMock } from './new_platform.test.mocks'; import { legacyAppRegister, __reset__, __setup__, __start__ } from './new_platform'; import { coreMock } from '../../../../core/public/mocks'; +import { AppMount } from '../../../../core/public'; describe('ui/new_platform', () => { describe('legacyAppRegister', () => { @@ -33,7 +34,7 @@ describe('ui/new_platform', () => { const registerApp = () => { const unmountMock = jest.fn(); - const mountMock = jest.fn(() => unmountMock); + const mountMock = jest.fn, Parameters>(() => unmountMock); legacyAppRegister({ id: 'test', title: 'Test', @@ -62,13 +63,25 @@ describe('ui/new_platform', () => { controller(scopeMock, elementMock); expect(mountMock).toHaveBeenCalledWith({ - element: elementMock[0], + element: expect.any(HTMLElement), appBasePath: '/test/base/path/app/test', onAppLeave: expect.any(Function), history: historyMock, }); }); + test('app is mounted in new div inside containing element', () => { + const { mountMock } = registerApp(); + const controller = setRootControllerMock.mock.calls[0][1]; + const scopeMock = { $on: jest.fn() }; + const elementMock = [document.createElement('div')]; + + controller(scopeMock, elementMock); + + const { element } = mountMock.mock.calls[0][0]; + expect(element.parentElement).toEqual(elementMock[0]); + }); + test('controller calls deprecated context app.mount when invoked', () => { const unmountMock = jest.fn(); // Two arguments changes how this is called. @@ -84,7 +97,7 @@ describe('ui/new_platform', () => { controller(scopeMock, elementMock); expect(mountMock).toHaveBeenCalledWith(expect.any(Object), { - element: elementMock[0], + element: expect.any(HTMLElement), appBasePath: '/test/base/path/app/test', onAppLeave: expect.any(Function), history: historyMock, diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index a15c7cce5511d..1eb46e1a43895 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -176,7 +176,8 @@ export const legacyAppRegister = (app: App) => { legacyAppRegistered = true; require('ui/chrome').setRootController(app.id, ($scope: IScope, $element: JQLite) => { - const element = $element[0]; + const element = document.createElement('div'); + $element[0].appendChild(element); // Root controller cannot return a Promise so use an internal async function and call it immediately (async () => { diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index 44733499cdcba..e093342f95735 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -31,7 +31,7 @@ export { } from './application'; export { DashboardConstants, createDashboardEditUrl } from './dashboard_constants'; -export { DashboardStart } from './plugin'; +export { DashboardStart, DashboardUrlGenerator } from './plugin'; export { DASHBOARD_APP_URL_GENERATOR } from './url_generator'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index b28822120b31e..894440cfa9cbb 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -42,7 +42,11 @@ import { DataPublicPluginSetup, esFilters, } from '../../../plugins/data/public'; -import { SharePluginSetup, SharePluginStart } from '../../../plugins/share/public'; +import { + SharePluginSetup, + SharePluginStart, + UrlGeneratorContract, +} from '../../../plugins/share/public'; import { UiActionsSetup, UiActionsStart } from '../../../plugins/ui_actions/public'; import { Start as InspectorStartContract } from '../../../plugins/inspector/public'; @@ -77,7 +81,7 @@ import { import { DashboardAppLinkGeneratorState, DASHBOARD_APP_URL_GENERATOR, - createDirectAccessDashboardLinkGenerator, + createDashboardUrlGenerator, } from './url_generator'; import { createSavedDashboardLoader } from './saved_dashboards'; import { DashboardConstants } from './dashboard_constants'; @@ -89,6 +93,8 @@ declare module '../../share/public' { } } +export type DashboardUrlGenerator = UrlGeneratorContract; + interface SetupDependencies { data: DataPublicPluginSetup; embeddable: EmbeddableSetup; @@ -111,8 +117,10 @@ interface StartDependencies { } export type Setup = void; + export interface DashboardStart { getSavedDashboardLoader: () => SavedObjectLoader; + dashboardUrlGenerator?: DashboardUrlGenerator; } declare module '../../../plugins/ui_actions/public' { @@ -130,6 +138,8 @@ export class DashboardPlugin private appStateUpdater = new BehaviorSubject(() => ({})); private stopUrlTracking: (() => void) | undefined = undefined; + private dashboardUrlGenerator?: DashboardUrlGenerator; + public setup( core: CoreSetup, { share, uiActions, embeddable, home, kibanaLegacy, data, usageCollection }: SetupDependencies @@ -140,8 +150,8 @@ export class DashboardPlugin const startServices = core.getStartServices(); if (share) { - share.urlGenerators.registerUrlGenerator( - createDirectAccessDashboardLinkGenerator(async () => { + this.dashboardUrlGenerator = share.urlGenerators.registerUrlGenerator( + createDashboardUrlGenerator(async () => { const [coreStart, , selfStart] = await startServices; return { appBasePath: coreStart.application.getUrlForApp('dashboard'), @@ -325,6 +335,7 @@ export class DashboardPlugin }); return { getSavedDashboardLoader: () => savedDashboardLoader, + dashboardUrlGenerator: this.dashboardUrlGenerator, }; } diff --git a/src/plugins/dashboard/public/url_generator.test.ts b/src/plugins/dashboard/public/url_generator.test.ts index 248a3f991d6cb..68d447c4a1336 100644 --- a/src/plugins/dashboard/public/url_generator.test.ts +++ b/src/plugins/dashboard/public/url_generator.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createDirectAccessDashboardLinkGenerator } from './url_generator'; +import { createDashboardUrlGenerator } from './url_generator'; import { hashedItemStore } from '../../kibana_utils/public'; // eslint-disable-next-line import { mockStorage } from '../../kibana_utils/public/storage/hashed_item_store/mock'; @@ -55,7 +55,7 @@ describe('dashboard url generator', () => { }); test('creates a link to a saved dashboard', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, @@ -67,7 +67,7 @@ describe('dashboard url generator', () => { }); test('creates a link with global time range set up', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, @@ -83,7 +83,7 @@ describe('dashboard url generator', () => { }); test('creates a link with filters, time range, refresh interval and query to a saved object', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, @@ -123,7 +123,7 @@ describe('dashboard url generator', () => { }); test('if no useHash setting is given, uses the one was start services', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: true, @@ -137,7 +137,7 @@ describe('dashboard url generator', () => { }); test('can override a false useHash ui setting', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, @@ -152,7 +152,7 @@ describe('dashboard url generator', () => { }); test('can override a true useHash ui setting', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: true, @@ -195,7 +195,7 @@ describe('dashboard url generator', () => { }; test('attaches filters from destination dashboard', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, @@ -224,7 +224,7 @@ describe('dashboard url generator', () => { }); test("doesn't fail if can't retrieve filters from destination dashboard", async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, @@ -246,7 +246,7 @@ describe('dashboard url generator', () => { }); test('can enforce empty filters', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, @@ -270,7 +270,7 @@ describe('dashboard url generator', () => { }); test('no filters in result url if no filters applied', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, @@ -288,7 +288,7 @@ describe('dashboard url generator', () => { }); test('can turn off preserving filters', async () => { - const generator = createDirectAccessDashboardLinkGenerator(() => + const generator = createDashboardUrlGenerator(() => Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false, diff --git a/src/plugins/dashboard/public/url_generator.ts b/src/plugins/dashboard/public/url_generator.ts index 6f121ceb2d373..9d66f2df65777 100644 --- a/src/plugins/dashboard/public/url_generator.ts +++ b/src/plugins/dashboard/public/url_generator.ts @@ -75,7 +75,7 @@ export type DashboardAppLinkGeneratorState = UrlGeneratorState<{ preserveSavedFilters?: boolean; }>; -export const createDirectAccessDashboardLinkGenerator = ( +export const createDashboardUrlGenerator = ( getStartServices: () => Promise<{ appBasePath: string; useHashedUrl: boolean; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index df4ba23244b4d..1f4076aa12bde 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -93,8 +93,7 @@ import { IngestGetPipelineParams } from 'elasticsearch'; import { IngestPutPipelineParams } from 'elasticsearch'; import { IngestSimulateParams } from 'elasticsearch'; import { KibanaConfigType as KibanaConfigType_2 } from 'src/core/server/kibana_config'; -import { Logger as Logger_2 } from 'src/core/server/logging'; -import { Logger as Logger_3 } from 'kibana/server'; +import { Logger as Logger_2 } from 'kibana/server'; import { MGetParams } from 'elasticsearch'; import { MGetResponse } from 'elasticsearch'; import moment from 'moment'; diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx index 282b0f05891e0..3894d6fbed382 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx @@ -18,6 +18,7 @@ */ import * as React from 'react'; +import { EuiFlyout } from '@elastic/eui'; import { AddPanelFlyout } from './add_panel_flyout'; import { ContactCardEmbeddableFactory, @@ -75,6 +76,9 @@ test('createNewEmbeddable() add embeddable to container', async () => { /> ) as ReactWrapper; + // https://github.com/elastic/kibana/issues/64789 + expect(component.exists(EuiFlyout)).toBe(false); + expect(Object.values(container.getInput().panels).length).toBe(0); component.instance().createNewEmbeddable(CONTACT_CARD_EMBEDDABLE); await new Promise(r => setTimeout(r, 1)); diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx index 5bf3f69a95c30..4c23916675e8f 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx @@ -21,13 +21,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { ReactElement } from 'react'; import { CoreSetup } from 'src/core/public'; -import { - EuiContextMenuItem, - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutHeader, - EuiTitle, -} from '@elastic/eui'; +import { EuiContextMenuItem, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui'; import { EmbeddableStart } from 'src/plugins/embeddable/public'; import { IContainer } from '../../../../containers'; @@ -152,7 +146,7 @@ export class AddPanelFlyout extends React.Component { ); return ( - + <>

@@ -161,7 +155,7 @@ export class AddPanelFlyout extends React.Component { {savedObjectsFinder} - + ); } } diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx index a452e07b51577..867092b78ef7a 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx @@ -55,7 +55,8 @@ export async function openAddPanelFlyout(options: { /> ), { - 'data-test-subj': 'addPanelFlyout', + 'data-test-subj': 'dashboardAddPanel', + ownFocus: true, } ); } diff --git a/src/plugins/share/public/url_generators/url_generator_service.test.ts b/src/plugins/share/public/url_generators/url_generator_service.test.ts index 4a377db033762..d256dcf5f7aa0 100644 --- a/src/plugins/share/public/url_generators/url_generator_service.test.ts +++ b/src/plugins/share/public/url_generators/url_generator_service.test.ts @@ -30,11 +30,11 @@ test('Asking for a generator that does not exist throws an error', () => { }); test('Registering and retrieving a generator', async () => { - setup.registerUrlGenerator({ + const generator = setup.registerUrlGenerator({ id: 'TEST_GENERATOR', createUrl: () => Promise.resolve('myurl'), }); - const generator = start.getUrlGenerator('TEST_GENERATOR'); + expect(generator).toMatchInlineSnapshot(` Object { "createUrl": [Function], @@ -47,6 +47,20 @@ test('Registering and retrieving a generator', async () => { new Error('You cannot call migrate on a non-deprecated generator.') ); expect(await generator.createUrl({})).toBe('myurl'); + + const retrievedGenerator = start.getUrlGenerator('TEST_GENERATOR'); + expect(retrievedGenerator).toMatchInlineSnapshot(` + Object { + "createUrl": [Function], + "id": "TEST_GENERATOR", + "isDeprecated": false, + "migrate": [Function], + } + `); + await expect(generator.migrate({})).rejects.toEqual( + new Error('You cannot call migrate on a non-deprecated generator.') + ); + expect(await generator.createUrl({})).toBe('myurl'); }); test('Registering a generator with a createUrl function that is deprecated throws an error', () => { diff --git a/src/plugins/share/public/url_generators/url_generator_service.ts b/src/plugins/share/public/url_generators/url_generator_service.ts index 332750671cee3..13c1b94acdd07 100644 --- a/src/plugins/share/public/url_generators/url_generator_service.ts +++ b/src/plugins/share/public/url_generators/url_generator_service.ts @@ -28,7 +28,9 @@ export interface UrlGeneratorsStart { } export interface UrlGeneratorsSetup { - registerUrlGenerator: (generator: UrlGeneratorsDefinition) => void; + registerUrlGenerator: ( + generator: UrlGeneratorsDefinition + ) => UrlGeneratorContract; } export class UrlGeneratorsService implements Plugin { @@ -43,10 +45,9 @@ export class UrlGeneratorsService implements Plugin( generatorOptions: UrlGeneratorsDefinition ) => { - this.urlGenerators.set( - generatorOptions.id, - new UrlGeneratorInternal(generatorOptions, this.getUrlGenerator) - ); + const generator = new UrlGeneratorInternal(generatorOptions, this.getUrlGenerator); + this.urlGenerators.set(generatorOptions.id, generator); + return generator.getPublicContract(); }, }; return setup; diff --git a/src/plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx index 68348d5ef1060..837d478535936 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx @@ -147,5 +147,6 @@ function TableOptions({ ); } - -export { TableOptions }; +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { TableOptions as default }; diff --git a/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx b/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx new file mode 100644 index 0000000000000..ca273aa771ef1 --- /dev/null +++ b/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx @@ -0,0 +1,30 @@ +/* + * 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 React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { TableVisParams } from '../types'; + +const TableOptionsComponent = lazy(() => import('./table_vis_options')); + +export const TableOptions = (props: VisOptionsProps) => ( + }> + + +); diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts index 26e5ac8cfd71a..c3bc72497007e 100644 --- a/src/plugins/vis_type_table/public/table_vis_type.ts +++ b/src/plugins/vis_type_table/public/table_vis_type.ts @@ -24,7 +24,7 @@ import { Vis } from '../../visualizations/public'; import { tableVisResponseHandler } from './table_vis_response_handler'; // @ts-ignore import tableVisTemplate from './table_vis.html'; -import { TableOptions } from './components/table_vis_options'; +import { TableOptions } from './components/table_vis_options_lazy'; import { getTableVisualizationControllerClass } from './vis_controller'; export function getTableVisTypeDefinition(core: CoreSetup, context: PluginInitializerContext) { diff --git a/x-pack/plugins/dashboard_enhanced/public/plugin.ts b/x-pack/plugins/dashboard_enhanced/public/plugin.ts index 772e032289bce..c258a4148f84a 100644 --- a/x-pack/plugins/dashboard_enhanced/public/plugin.ts +++ b/x-pack/plugins/dashboard_enhanced/public/plugin.ts @@ -11,6 +11,7 @@ import { DashboardDrilldownsService } from './services'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { AdvancedUiActionsSetup, AdvancedUiActionsStart } from '../../advanced_ui_actions/public'; import { DrilldownsSetup, DrilldownsStart } from '../../drilldowns/public'; +import { DashboardStart } from '../../../../src/plugins/dashboard/public'; export interface SetupDependencies { advancedUiActions: AdvancedUiActionsSetup; @@ -25,6 +26,7 @@ export interface StartDependencies { drilldowns: DrilldownsStart; embeddable: EmbeddableStart; share: SharePluginStart; + dashboard: DashboardStart; } // eslint-disable-next-line diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts index 0161836b2c5b9..f5926cd6961c2 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts @@ -44,6 +44,12 @@ export class DashboardDrilldownsService { { advancedUiActions: uiActions }: SetupDependencies ) { const start = createStartServicesGetter(core.getStartServices); + const getDashboardUrlGenerator = () => { + const urlGenerator = start().plugins.dashboard.dashboardUrlGenerator; + if (!urlGenerator) + throw new Error('dashboardUrlGenerator is required for dashboard to dashboard drilldown'); + return urlGenerator; + }; const actionFlyoutCreateDrilldown = new FlyoutCreateDrilldownAction({ start }); uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, actionFlyoutCreateDrilldown); @@ -51,7 +57,10 @@ export class DashboardDrilldownsService { const actionFlyoutEditDrilldown = new FlyoutEditDrilldownAction({ start }); uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, actionFlyoutEditDrilldown); - const dashboardToDashboardDrilldown = new DashboardToDashboardDrilldown({ start }); + const dashboardToDashboardDrilldown = new DashboardToDashboardDrilldown({ + start, + getDashboardUrlGenerator, + }); uiActions.registerDrilldown(dashboardToDashboardDrilldown); } } diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx index 18ee95cb57b3b..d8465562f9302 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx @@ -5,8 +5,7 @@ */ import { DashboardToDashboardDrilldown } from './drilldown'; -import { UrlGeneratorContract } from '../../../../../../../src/plugins/share/public'; -import { savedObjectsServiceMock } from '../../../../../../../src/core/public/mocks'; +import { savedObjectsServiceMock, coreMock } from '../../../../../../../src/core/public/mocks'; import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; import { ActionContext, Config } from './types'; import { @@ -19,15 +18,16 @@ import { import { esFilters } from '../../../../../../../src/plugins/data/public'; // convenient to use real implementation here. -import { createDirectAccessDashboardLinkGenerator } from '../../../../../../../src/plugins/dashboard/public/url_generator'; +import { createDashboardUrlGenerator } from '../../../../../../../src/plugins/dashboard/public/url_generator'; +import { UrlGeneratorsService } from '../../../../../../../src/plugins/share/public/url_generators'; import { VisualizeEmbeddableContract } from '../../../../../../../src/plugins/visualizations/public'; import { RangeSelectTriggerContext, ValueClickTriggerContext, } from '../../../../../../../src/plugins/embeddable/public'; +import { StartDependencies } from '../../../plugin'; import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public'; import { StartServicesGetter } from '../../../../../../../src/plugins/kibana_utils/public/core'; -import { StartDependencies } from '../../../plugin'; describe('.isConfigValid()', () => { const drilldown = new DashboardToDashboardDrilldown({} as any); @@ -105,23 +105,19 @@ describe('.execute() & getHref', () => { data: { actions: dataPluginActions, }, - share: { - urlGenerators: { - getUrlGenerator: () => - createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ - appBasePath: 'test', - useHashedUrl: false, - savedDashboardLoader: ({} as unknown) as SavedObjectLoader, - }) - ) as UrlGeneratorContract, - }, - }, }, self: {}, - })) as unknown) as StartServicesGetter< - Pick - >, + })) as unknown) as StartServicesGetter>, + getDashboardUrlGenerator: () => + new UrlGeneratorsService().setup(coreMock.createSetup()).registerUrlGenerator( + createDashboardUrlGenerator(() => + Promise.resolve({ + appBasePath: 'test', + useHashedUrl: false, + savedDashboardLoader: ({} as unknown) as SavedObjectLoader, + }) + ) + ), }); const selectRangeFiltersSpy = jest .spyOn(dataPluginActions, 'createFiltersFromRangeSelectAction') diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx index 848e77384f7f0..6d83b8443a828 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { reactToUiComponent } from '../../../../../../../src/plugins/kibana_react/public'; -import { DASHBOARD_APP_URL_GENERATOR } from '../../../../../../../src/plugins/dashboard/public'; +import { DashboardUrlGenerator } from '../../../../../../../src/plugins/dashboard/public'; import { ActionContext, Config } from './types'; import { CollectConfigContainer } from './components'; import { DASHBOARD_TO_DASHBOARD_DRILLDOWN } from './constants'; @@ -22,7 +22,8 @@ import { StartServicesGetter } from '../../../../../../../src/plugins/kibana_uti import { StartDependencies } from '../../../plugin'; export interface Params { - start: StartServicesGetter>; + start: StartServicesGetter>; + getDashboardUrlGenerator: () => DashboardUrlGenerator; } export class DashboardToDashboardDrilldown @@ -142,9 +143,7 @@ export class DashboardToDashboardDrilldown } } - const { plugins } = this.params.start(); - - return plugins.share.urlGenerators.getUrlGenerator(DASHBOARD_APP_URL_GENERATOR).createUrl({ + return this.params.getDashboardUrlGenerator().createUrl({ dashboardId: config.dashboardId, query: config.useCurrentFilters ? query : undefined, timeRange, diff --git a/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx b/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx index 0d4a67e325e4d..5ebda079a15bf 100644 --- a/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx +++ b/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx @@ -289,8 +289,8 @@ function useDrilldownsStateManager( await run(async () => { await actionManager.createEvent(action, selectedTriggers); notifications.toasts.addSuccess({ - title: toastDrilldownCreated.title, - text: toastDrilldownCreated.text(action.name), + title: toastDrilldownCreated.title(action.name), + text: toastDrilldownCreated.text, }); }); } @@ -303,8 +303,8 @@ function useDrilldownsStateManager( await run(async () => { await actionManager.updateEvent(drilldownId, action, selectedTriggers); notifications.toasts.addSuccess({ - title: toastDrilldownEdited.title, - text: toastDrilldownEdited.text(action.name), + title: toastDrilldownEdited.title(action.name), + text: toastDrilldownEdited.text, }); }); } @@ -320,8 +320,8 @@ function useDrilldownsStateManager( text: toastDrilldownDeleted.text, } : { - title: toastDrilldownsDeleted.title, - text: toastDrilldownsDeleted.text(drilldownIds.length), + title: toastDrilldownsDeleted.title(drilldownIds.length), + text: toastDrilldownsDeleted.text, } ); }); diff --git a/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/i18n.ts b/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/i18n.ts index 31384860786ef..851439eccbe7e 100644 --- a/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/i18n.ts +++ b/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/i18n.ts @@ -7,35 +7,41 @@ import { i18n } from '@kbn/i18n'; export const toastDrilldownCreated = { - title: i18n.translate( - 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownCreatedTitle', + title: (drilldownName: string) => + i18n.translate( + 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownCreatedTitle', + { + defaultMessage: 'Drilldown "{drilldownName}" created', + values: { + drilldownName, + }, + } + ), + text: i18n.translate( + 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownCreatedText', { - defaultMessage: 'Drilldown created', + // TODO: remove `Save your dashboard before testing.` part + // when drilldowns are used not only in dashboard + // or after https://github.com/elastic/kibana/issues/65179 implemented + defaultMessage: 'Save your dashboard before testing.', } ), - text: (drilldownName: string) => - i18n.translate('xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownCreatedText', { - defaultMessage: 'You created "{drilldownName}". Save dashboard before testing.', - values: { - drilldownName, - }, - }), }; export const toastDrilldownEdited = { - title: i18n.translate( - 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownEditedTitle', - { - defaultMessage: 'Drilldown edited', - } - ), - text: (drilldownName: string) => - i18n.translate('xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownEditedText', { - defaultMessage: 'You edited "{drilldownName}". Save dashboard before testing.', + title: (drilldownName: string) => + i18n.translate('xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownEditedTitle', { + defaultMessage: 'Drilldown "{drilldownName}" updated', values: { drilldownName, }, }), + text: i18n.translate( + 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownEditedText', + { + defaultMessage: 'Save your dashboard before testing.', + } + ), }; export const toastDrilldownDeleted = { @@ -48,28 +54,26 @@ export const toastDrilldownDeleted = { text: i18n.translate( 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownDeletedText', { - defaultMessage: 'You deleted a drilldown.', + defaultMessage: 'Save your dashboard before testing.', } ), }; export const toastDrilldownsDeleted = { - title: i18n.translate( - 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownsDeletedTitle', - { - defaultMessage: 'Drilldowns deleted', - } - ), - text: (n: number) => + title: (n: number) => i18n.translate( - 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownsDeletedText', + 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownsDeletedTitle', { - defaultMessage: 'You deleted {n} drilldowns', - values: { - n, - }, + defaultMessage: '{n} drilldowns deleted', + values: { n }, } ), + text: i18n.translate( + 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownsDeletedText', + { + defaultMessage: 'Save your dashboard before testing.', + } + ), }; export const toastDrilldownsCRUDError = i18n.translate( @@ -79,10 +83,3 @@ export const toastDrilldownsCRUDError = i18n.translate( description: 'Title for generic error toast when persisting drilldown updates failed', } ); - -export const toastDrilldownsFetchError = i18n.translate( - 'xpack.drilldowns.components.flyoutDrilldownWizard.toast.drilldownsFetchErrorTitle', - { - defaultMessage: 'Error fetching drilldowns', - } -); diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/i18n.ts b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/i18n.ts index 63dc95dabc0fb..622376c5b40ad 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/i18n.ts +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/i18n.ts @@ -10,7 +10,7 @@ export const txtHelpText = i18n.translate( 'xpack.drilldowns.components.DrilldownHelloBar.helpText', { defaultMessage: - 'Drilldowns provide the ability to define a new behavior when interacting with a panel. You can add multiple options or simply override the default filtering behavior.', + 'Drilldowns enable you to define new behaviors for interacting with panels. You can add multiple actions and override the default filter.', } ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx index 818b365d5be12..2f06d1d8703c2 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx @@ -13,6 +13,7 @@ import { // @ts-ignore EuiSearchBar, EuiText, + Query, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -35,9 +36,28 @@ export function PackageListGrid({ list, showInstalledBadge, }: ListProps) { + const initialQuery = EuiSearchBar.Query.MATCH_ALL; + + const [query, setQuery] = useState(initialQuery); const [searchTerm, setSearchTerm] = useState(''); const localSearchRef = useLocalSearch(list); + const onQueryChange = ({ + // eslint-disable-next-line no-shadow + query, + queryText: userInput, + error, + }: { + query: Query | null; + queryText: string; + error: { message: string } | null; + }) => { + if (!error) { + setQuery(query); + setSearchTerm(userInput); + } + }; + const controlsContent = ; let gridContent: JSX.Element; @@ -59,16 +79,14 @@ export function PackageListGrid({ {controlsContent} { - setSearchTerm(userInput); - }} + onChange={onQueryChange} /> {gridContent} diff --git a/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts b/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts index 9d3eb5360dbe3..94fc6de609613 100644 --- a/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts +++ b/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts @@ -58,6 +58,12 @@ export const postEnrollmentApiKeyHandler: RequestHandler< return response.ok({ body }); } catch (e) { + if (e.isBoom) { + return response.customError({ + statusCode: e.output.statusCode, + body: { message: e.message }, + }); + } return response.customError({ statusCode: 500, body: { message: e.message }, diff --git a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts index 1ac812c3380cd..3b003f47eb6f9 100644 --- a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts @@ -5,6 +5,7 @@ */ import uuid from 'uuid'; +import Boom from 'boom'; import { SavedObjectsClientContract, SavedObject } from 'src/core/server'; import { EnrollmentAPIKey, EnrollmentAPIKeySOAttributes } from '../../types'; import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; @@ -106,6 +107,9 @@ export async function generateEnrollmentAPIKey( ) { const id = uuid.v4(); const { name: providedKeyName } = data; + if (data.configId) { + await validateConfigId(soClient, data.configId); + } const configId = data.configId ?? (await agentConfigService.getDefaultAgentConfigId(soClient)); const name = providedKeyName ? `${providedKeyName} (${id})` : id; const key = await createAPIKey(soClient, name, { @@ -143,6 +147,17 @@ export async function generateEnrollmentAPIKey( return getEnrollmentAPIKey(soClient, so.id); } +async function validateConfigId(soClient: SavedObjectsClientContract, configId: string) { + try { + await agentConfigService.get(soClient, configId); + } catch (e) { + if (e.isBoom && e.output.statusCode === 404) { + throw Boom.badRequest(`Agent config ${configId} does not exist`); + } + throw e; + } +} + function savedObjectToEnrollmentApiKey({ error, attributes, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js index 29e89022a5502..2a65ee06f2c2c 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js @@ -157,6 +157,7 @@ class CreateWatchService { id, type: 'json', isNew: false, // Set to false, as we want to allow watches to be overwritten. + isActive: true, watch, }, }; diff --git a/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts b/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts index ca127f43d08af..d907677855c12 100644 --- a/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts +++ b/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts @@ -14,6 +14,13 @@ const callWithRequest: APICaller = (method: string) => { if (method === 'fieldCaps') { resolve({ fields: [] }); return; + } else if (method === 'ml.info') { + resolve({ + limits: { + effective_max_model_memory_limit: '100MB', + max_model_memory_limit: '1GB', + }, + }); } resolve({}); }) as Promise; @@ -291,7 +298,7 @@ describe('ML - validateJob', () => { }); // Failing https://github.com/elastic/kibana/issues/65865 - it.skip('basic validation passes, extended checks return some messages', () => { + it('basic validation passes, extended checks return some messages', () => { const payload = getBasicPayload(); return validateJob(callWithRequest, payload).then(messages => { const ids = messages.map(m => m.id); @@ -305,7 +312,7 @@ describe('ML - validateJob', () => { }); // Failing https://github.com/elastic/kibana/issues/65866 - it.skip('categorization job using mlcategory passes aggregatable field check', () => { + it('categorization job using mlcategory passes aggregatable field check', () => { const payload: any = { job: { job_id: 'categorization_test', @@ -372,7 +379,7 @@ describe('ML - validateJob', () => { }); // Failing https://github.com/elastic/kibana/issues/65867 - it.skip('script field not reported as non aggregatable', () => { + it('script field not reported as non aggregatable', () => { const payload: any = { job: { job_id: 'categorization_test', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 3440bb28b2468..8511ab468ca80 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -127,26 +127,27 @@ export const AlertDetails: React.FunctionComponent = ({ defaultMessage="Edit" /> - - - + {editFlyoutVisible && ( + + setEditFlyoutVisibility(false)} + /> + + )} ) : null} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx index 722db146a54ce..4d8801d8b7484 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx @@ -131,11 +131,7 @@ describe('alert_edit', () => { capabilities: deps!.capabilities, }} > - {}} - initialAlert={alert} - /> + {}} initialAlert={alert} /> ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 00bc9874face1..747464d2212f4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -31,15 +31,10 @@ import { PLUGIN } from '../../constants/plugin'; interface AlertEditProps { initialAlert: Alert; - editFlyoutVisible: boolean; - setEditFlyoutVisibility: React.Dispatch>; + onClose(): void; } -export const AlertEdit = ({ - initialAlert, - editFlyoutVisible, - setEditFlyoutVisibility, -}: AlertEditProps) => { +export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert }); const [isSaving, setIsSaving] = useState(false); const [hasActionsDisabled, setHasActionsDisabled] = useState(false); @@ -57,14 +52,10 @@ export const AlertEdit = ({ } = useAlertsContext(); const closeFlyout = useCallback(() => { - setEditFlyoutVisibility(false); + onClose(); setAlert('alert', initialAlert); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setEditFlyoutVisibility]); - - if (!editFlyoutVisible) { - return null; - } + }, [onClose]); const alertType = alertTypeRegistry.get(alert.alertTypeId); diff --git a/x-pack/plugins/uptime/common/translations.ts b/x-pack/plugins/uptime/common/translations.ts new file mode 100644 index 0000000000000..678fe7cb1f984 --- /dev/null +++ b/x-pack/plugins/uptime/common/translations.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const VALUE_MUST_BE_GREATER_THEN_ZEO = i18n.translate( + 'xpack.uptime.settings.invalid.error', + { + defaultMessage: 'Value must be greater than 0.', + } +); diff --git a/x-pack/plugins/uptime/public/components/settings/certificate_form.tsx b/x-pack/plugins/uptime/public/components/settings/certificate_form.tsx index 5bfd26f7c6401..8200e8292cd98 100644 --- a/x-pack/plugins/uptime/public/components/settings/certificate_form.tsx +++ b/x-pack/plugins/uptime/public/components/settings/certificate_form.tsx @@ -59,7 +59,7 @@ export const CertificateExpirationForm: React.FC = ({ > = ({ }} /> } - isInvalid={!!fieldErrors?.certificatesThresholds?.expirationThresholdError} + isInvalid={!!fieldErrors?.expirationThresholdError} label={ = ({ = ({ = ({ }} /> } - isInvalid={!!fieldErrors?.certificatesThresholds?.ageThresholdError} + isInvalid={!!fieldErrors?.ageThresholdError} label={ = ({ + onChange={({ currentTarget: { value } }) => onChange({ - certAgeThreshold: Number(e.currentTarget.value), + certAgeThreshold: Number(value), }) } /> diff --git a/x-pack/plugins/uptime/public/pages/settings.tsx b/x-pack/plugins/uptime/public/pages/settings.tsx index 7ed9cf4444343..d018567ae1104 100644 --- a/x-pack/plugins/uptime/public/pages/settings.tsx +++ b/x-pack/plugins/uptime/public/pages/settings.tsx @@ -32,13 +32,12 @@ import { OnFieldChangeType, } from '../components/settings/certificate_form'; import * as Translations from './translations'; +import { VALUE_MUST_BE_GREATER_THEN_ZEO } from '../../common/translations'; interface SettingsPageFieldErrors { - heartbeatIndices: 'May not be blank' | ''; - certificatesThresholds: { - expirationThresholdError: string | null; - ageThresholdError: string | null; - } | null; + heartbeatIndices: string | ''; + expirationThresholdError?: string; + ageThresholdError?: string; } export interface SettingsFormProps { @@ -49,22 +48,28 @@ export interface SettingsFormProps { isDisabled: boolean; } +const isValidCertVal = (val: string | number) => { + if (val === '') { + return Translations.BLANK_STR; + } + if (val === 0) { + return VALUE_MUST_BE_GREATER_THEN_ZEO; + } +}; + const getFieldErrors = (formFields: DynamicSettings | null): SettingsPageFieldErrors | null => { if (formFields) { - const blankStr = 'May not be blank'; const { certAgeThreshold, certExpirationThreshold, heartbeatIndices } = formFields; - const heartbeatIndErr = heartbeatIndices.match(/^\S+$/) ? '' : blankStr; - const expirationThresholdError = certExpirationThreshold ? null : blankStr; - const ageThresholdError = certAgeThreshold ? null : blankStr; + + const indError = heartbeatIndices.match(/^\S+$/) ? '' : Translations.BLANK_STR; + + const expError = isValidCertVal(certExpirationThreshold); + const ageError = isValidCertVal(certAgeThreshold); + return { - heartbeatIndices: heartbeatIndErr, - certificatesThresholds: - expirationThresholdError || ageThresholdError - ? { - expirationThresholdError, - ageThresholdError, - } - : null, + heartbeatIndices: indError, + expirationThresholdError: expError, + ageThresholdError: ageError, }; } return null; diff --git a/x-pack/plugins/uptime/public/pages/translations.ts b/x-pack/plugins/uptime/public/pages/translations.ts index 74fb2eeb1416b..8ed5503235884 100644 --- a/x-pack/plugins/uptime/public/pages/translations.ts +++ b/x-pack/plugins/uptime/public/pages/translations.ts @@ -36,3 +36,7 @@ export const settings = { defaultMessage: 'Return to overview', }), }; + +export const BLANK_STR = i18n.translate('xpack.uptime.settings.blank.error', { + defaultMessage: 'May not be blank.', +}); diff --git a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts index 31833a25ee8ac..c7d532d932aa6 100644 --- a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts +++ b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts @@ -11,6 +11,7 @@ import { UMServerLibs } from '../lib/lib'; import { DynamicSettings, DynamicSettingsType } from '../../common/runtime_types'; import { UMRestApiRouteFactory } from '.'; import { savedObjectsAdapter } from '../lib/saved_objects'; +import { VALUE_MUST_BE_GREATER_THEN_ZEO } from '../../common/translations'; export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', @@ -23,19 +24,35 @@ export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSer }, }); +const validateCertsValues = (settings: DynamicSettings) => { + const errors: any = {}; + if (settings.certAgeThreshold <= 0) { + errors.certAgeThreshold = VALUE_MUST_BE_GREATER_THEN_ZEO; + } + if (settings.certExpirationThreshold <= 0) { + errors.certExpirationThreshold = VALUE_MUST_BE_GREATER_THEN_ZEO; + } + if (errors.certAgeThreshold || errors.certExpirationThreshold) { + return errors; + } +}; + export const createPostDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'POST', path: '/api/uptime/dynamic_settings', validate: { - body: schema.object({}, { unknowns: 'allow' }), + body: schema.object({ + heartbeatIndices: schema.string(), + certAgeThreshold: schema.number(), + certExpirationThreshold: schema.number(), + }), }, writeAccess: true, - options: { - tags: ['access:uptime-write'], - }, handler: async ({ savedObjectsClient }, _context, request, response): Promise => { const decoded = DynamicSettingsType.decode(request.body); - if (isRight(decoded)) { + const certThresholdErrors = validateCertsValues(request.body as DynamicSettings); + + if (isRight(decoded) && !certThresholdErrors) { const newSettings: DynamicSettings = decoded.right; await savedObjectsAdapter.setUptimeDynamicSettings(savedObjectsClient, newSettings); @@ -47,7 +64,7 @@ export const createPostDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSe } else { const error = PathReporter.report(decoded).join(', '); return response.badRequest({ - body: error, + body: JSON.stringify(certThresholdErrors) || error, }); } }, diff --git a/x-pack/test/api_integration/apis/fleet/enrollment_api_keys/crud.ts b/x-pack/test/api_integration/apis/fleet/enrollment_api_keys/crud.ts index 602ec6ca9d9e4..9d0629a7b32e2 100644 --- a/x-pack/test/api_integration/apis/fleet/enrollment_api_keys/crud.ts +++ b/x-pack/test/api_integration/apis/fleet/enrollment_api_keys/crud.ts @@ -25,6 +25,7 @@ export default function(providerContext: FtrProviderContext) { after(async () => { await esArchiver.unload('fleet/agents'); }); + describe('GET /fleet/enrollment-api-keys', async () => { it('should list existing api keys', async () => { const { body: apiResponse } = await supertest @@ -54,7 +55,7 @@ export default function(providerContext: FtrProviderContext) { .post(`/api/ingest_manager/fleet/enrollment-api-keys`) .set('kbn-xsrf', 'xxx') .send({ - config_id: 'policy1', + config_id: 'config1', }) .expect(200); keyId = apiResponse.item.id; @@ -89,12 +90,22 @@ export default function(providerContext: FtrProviderContext) { .expect(400); }); - it('should allow to create an enrollment api key with a policy', async () => { + it('should not allow to create an enrollment api key for a non existing agent config', async () => { + await supertest + .post(`/api/ingest_manager/fleet/enrollment-api-keys`) + .set('kbn-xsrf', 'xxx') + .send({ + config_id: 'idonotexistsconfig', + }) + .expect(400); + }); + + it('should allow to create an enrollment api key with an agent config', async () => { const { body: apiResponse } = await supertest .post(`/api/ingest_manager/fleet/enrollment-api-keys`) .set('kbn-xsrf', 'xxx') .send({ - config_id: 'policy1', + config_id: 'config1', }) .expect(200); @@ -107,7 +118,7 @@ export default function(providerContext: FtrProviderContext) { .post(`/api/ingest_manager/fleet/enrollment-api-keys`) .set('kbn-xsrf', 'xxx') .send({ - config_id: 'policy1', + config_id: 'config1', }) .expect(200); expect(apiResponse.success).to.eql(true); diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts new file mode 100644 index 0000000000000..255afecde74cb --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts @@ -0,0 +1,222 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/machine_learning/security_common'; + +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const jobId = `fq_single_${Date.now()}`; + + async function createJobs() { + const mockJobConfigs = [ + { + job_id: `${jobId}_1`, + description: + 'Single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)', + groups: ['automated', 'farequote', 'single-metric'], + analysis_config: { + bucket_span: '30m', + detectors: [{ function: 'mean', field_name: 'responsetime' }], + influencers: [], + summary_count_field_name: 'doc_count', + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '11MB' }, + model_plot_config: { enabled: true }, + }, + { + job_id: `${jobId}_2`, + description: + 'Another single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)', + groups: ['automated', 'farequote', 'single-metric'], + analysis_config: { + bucket_span: '30m', + detectors: [{ function: 'mean', field_name: 'responsetime' }], + influencers: [], + summary_count_field_name: 'doc_count', + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '11MB' }, + model_plot_config: { enabled: false }, + }, + ]; + + for (const jobConfig of mockJobConfigs) { + await ml.api.createAnomalyDetectionJob(jobConfig); + } + } + + describe('GET anomaly_detectors', () => { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + await createJobs(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + describe('GetAnomalyDetectors', () => { + it('should fetch all anomaly detector jobs', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_HEADERS) + .expect(200); + + expect(body.count).to.eql(2); + expect(body.jobs.length).to.eql(2); + expect(body.jobs[0].job_id).to.eql(`${jobId}_1`); + expect(body.jobs[1].job_id).to.eql(`${jobId}_2`); + }); + + it('should not allow to retrieve jobs for the user without required permissions', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_HEADERS) + .expect(404); + + expect(body.error).to.eql('Not Found'); + expect(body.message).to.eql('Not Found'); + }); + }); + + describe('GetAnomalyDetectorsById', () => { + it('should fetch single anomaly detector job by id', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors/${jobId}_1`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_HEADERS) + .expect(200); + + expect(body.count).to.eql(1); + expect(body.jobs.length).to.eql(1); + expect(body.jobs[0].job_id).to.eql(`${jobId}_1`); + }); + + it('should fetch anomaly detector jobs based on provided ids', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors/${jobId}_1,${jobId}_2`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_HEADERS) + .expect(200); + + expect(body.count).to.eql(2); + expect(body.jobs.length).to.eql(2); + expect(body.jobs[0].job_id).to.eql(`${jobId}_1`); + expect(body.jobs[1].job_id).to.eql(`${jobId}_2`); + }); + + it('should not allow to retrieve a job for the user without required permissions', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors/${jobId}_1`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_HEADERS) + .expect(404); + + expect(body.error).to.eql('Not Found'); + expect(body.message).to.eql('Not Found'); + }); + }); + + describe('GetAnomalyDetectorsStats', () => { + it('should fetch jobs stats', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors/_stats`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_HEADERS) + .expect(200); + + expect(body.count).to.eql(2); + expect(body.jobs.length).to.eql(2); + expect(body.jobs[0].job_id).to.eql(`${jobId}_1`); + expect(body.jobs[0]).to.keys( + 'timing_stats', + 'state', + 'forecasts_stats', + 'model_size_stats', + 'data_counts' + ); + expect(body.jobs[1].job_id).to.eql(`${jobId}_2`); + }); + + it('should not allow to retrieve jobs stats for the user without required permissions', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors/_stats`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_HEADERS) + .expect(404); + + expect(body.error).to.eql('Not Found'); + expect(body.message).to.eql('Not Found'); + }); + }); + + describe('GetAnomalyDetectorsStatsById', () => { + it('should fetch single job stats', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors/${jobId}_1/_stats`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_HEADERS) + .expect(200); + + expect(body.count).to.eql(1); + expect(body.jobs.length).to.eql(1); + expect(body.jobs[0].job_id).to.eql(`${jobId}_1`); + expect(body.jobs[0]).to.keys( + 'timing_stats', + 'state', + 'forecasts_stats', + 'model_size_stats', + 'data_counts' + ); + }); + + it('should fetch multiple jobs stats based on provided ids', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors/${jobId}_1,${jobId}_2/_stats`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_HEADERS) + .expect(200); + + expect(body.count).to.eql(2); + expect(body.jobs.length).to.eql(2); + expect(body.jobs[0].job_id).to.eql(`${jobId}_1`); + expect(body.jobs[0]).to.keys( + 'timing_stats', + 'state', + 'forecasts_stats', + 'model_size_stats', + 'data_counts' + ); + expect(body.jobs[1].job_id).to.eql(`${jobId}_2`); + }); + + it('should not allow to retrieve a job stats for the user without required permissions', async () => { + const { body } = await supertest + .get(`/api/ml/anomaly_detectors/${jobId}_1/_stats`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_HEADERS) + .expect(404); + + expect(body.error).to.eql('Not Found'); + expect(body.message).to.eql('Not Found'); + }); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/index.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/index.ts index fb8acaf5c3ae9..3985cd39e4d1c 100644 --- a/x-pack/test/api_integration/apis/ml/anomaly_detectors/index.ts +++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/index.ts @@ -8,5 +8,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { describe('anomaly detectors', function() { loadTestFile(require.resolve('./create')); + loadTestFile(require.resolve('./get')); }); } diff --git a/x-pack/test/api_integration/apis/ml/index.ts b/x-pack/test/api_integration/apis/ml/index.ts index 58356637c63ac..df99b125e6adb 100644 --- a/x-pack/test/api_integration/apis/ml/index.ts +++ b/x-pack/test/api_integration/apis/ml/index.ts @@ -37,5 +37,6 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./fields_service')); loadTestFile(require.resolve('./job_validation')); loadTestFile(require.resolve('./jobs')); + loadTestFile(require.resolve('./results')); }); } diff --git a/x-pack/test/api_integration/apis/ml/results/get_anomalies_table_data.ts b/x-pack/test/api_integration/apis/ml/results/get_anomalies_table_data.ts new file mode 100644 index 0000000000000..9f34a3c639562 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/results/get_anomalies_table_data.ts @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { USER } from '../../../../functional/services/machine_learning/security_common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { Datafeed, Job } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', + }; + + const JOB_CONFIG: Job = { + job_id: `fq_multi_1_ae`, + description: + 'mean/min/max(responsetime) partition=airline on farequote dataset with 1h bucket span', + groups: ['farequote', 'automated', 'multi-metric'], + analysis_config: { + bucket_span: '1h', + influencers: ['airline'], + detectors: [ + { function: 'mean', field_name: 'responsetime', partition_field_name: 'airline' }, + { function: 'min', field_name: 'responsetime', partition_field_name: 'airline' }, + { function: 'max', field_name: 'responsetime', partition_field_name: 'airline' }, + ], + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '20mb' }, + model_plot_config: { enabled: true }, + }; + + const DATAFEED_CONFIG: Datafeed = { + datafeed_id: 'datafeed-fq_multi_1_se', + indices: ['ft_farequote'], + job_id: 'fq_multi_1_ae', + query: { bool: { must: [{ match_all: {} }] } }, + }; + + async function createMockJobs() { + await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG); + } + + describe('GetAnomaliesTableData', () => { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await ml.testResources.setKibanaTimeZoneToUTC(); + await createMockJobs(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('should fetch anomalies table data', async () => { + const requestBody = { + jobIds: [JOB_CONFIG.job_id], + criteriaFields: [{ fieldName: 'detector_index', fieldValue: 0 }], + influencers: [], + aggregationInterval: 'auto', + threshold: 0, + earliestMs: 1454889600000, // February 8, 2016 12:00:00 AM GMT + latestMs: 1454976000000, // February 9, 2016 12:00:00 AM GMT + dateFormatTz: 'UTC', + maxRecords: 500, + }; + + const { body } = await supertest + .post(`/api/ml/results/anomalies_table_data`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_HEADERS) + .send(requestBody) + .expect(200); + + expect(body.interval).to.eql('hour'); + expect(body.anomalies.length).to.eql(12); + }); + + it('should validate request body', async () => { + const requestBody = { + // missing jobIds + criteriaFields: [{ fieldName: 'detector_index', fieldValue: 0 }], + influencers: [], + aggregationInterval: 'auto', + threshold: 0, + // invalid earliest and latest instead of earliestMs and latestMs + earliest: 1454889600000, // February 8, 2016 12:00:00 AM GMT + latest: 1454976000000, // February 9, 2016 12:00:00 AM GMT + dateFormatTz: 'UTC', + maxRecords: 500, + }; + + const { body } = await supertest + .post(`/api/ml/results/anomalies_table_data`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_HEADERS) + .send(requestBody) + .expect(400); + + expect(body.error).to.eql('Bad Request'); + expect(body.message).to.eql( + '[request body.jobIds]: expected value of type [array] but got [undefined]' + ); + }); + + it('should not allow fetching of anomalies table data without required permissions', async () => { + const requestBody = { + jobIds: [JOB_CONFIG.job_id], + criteriaFields: [{ fieldName: 'detector_index', fieldValue: 0 }], + influencers: [], + aggregationInterval: 'auto', + threshold: 0, + earliestMs: 1454889600000, // February 8, 2016 12:00:00 AM GMT + latestMs: 1454976000000, // February 9, 2016 12:00:00 AM GMT + dateFormatTz: 'UTC', + maxRecords: 500, + }; + const { body } = await supertest + .post(`/api/ml/results/anomalies_table_data`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_HEADERS) + .send(requestBody) + .expect(404); + + expect(body.error).to.eql('Not Found'); + expect(body.message).to.eql('Not Found'); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/results/index.ts b/x-pack/test/api_integration/apis/ml/results/index.ts new file mode 100644 index 0000000000000..80197e6a32ad9 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/results/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('ResultsService', () => { + loadTestFile(require.resolve('./get_anomalies_table_data')); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/_async_dashboard.ts b/x-pack/test/functional/apps/dashboard/_async_dashboard.ts index 2c8abda999ddd..82641db457aea 100644 --- a/x-pack/test/functional/apps/dashboard/_async_dashboard.ts +++ b/x-pack/test/functional/apps/dashboard/_async_dashboard.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); + const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); const log = getService('log'); const pieChart = getService('pieChart'); @@ -93,6 +94,9 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { ]`; await kibanaServer.uiSettings.update({ 'timepicker:quickRanges': SAMPLE_DATA_RANGE }); + // refresh page to make sure ui settings update is picked up + await browser.refresh(); + await PageObjects.header.waitUntilLoadingHasFinished(); await appMenu.clickLink('Discover'); await PageObjects.discover.selectIndexPattern('kibana_sample_data_flights'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); @@ -104,6 +108,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await PageObjects.common.navigateToUrl('home', 'tutorial_directory/sampleData'); + await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.home.removeSampleDataSet('flights'); const isInstalled = await PageObjects.home.isSampleDataSetInstalled('flights'); expect(isInstalled).to.be(false); diff --git a/x-pack/test/functional/es_archives/fleet/agents/data.json b/x-pack/test/functional/es_archives/fleet/agents/data.json index d22e3cd3fecdd..1739f583b2e87 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/data.json +++ b/x-pack/test/functional/es_archives/fleet/agents/data.json @@ -199,3 +199,30 @@ } } } + +{ + "type": "doc", + "value": { + "id": "ingest-agent-configs:config1", + "index": ".kibana", + "source": { + "type": "ingest-agent-configs", + "ingest-agent-configs": { + "name": "Test config", + "namespace": "default", + "description": "Config 1", + "status": "active", + "datasources": [], + "is_default": true, + "monitoring_enabled": [ + "logs", + "metrics" + ], + "revision": 2, + "updated_on": "2020-05-07T19:34:42.533Z", + "updated_by": "system", + "id": "config1" + } + } + } +} diff --git a/x-pack/test/functional/es_archives/fleet/agents/mappings.json b/x-pack/test/functional/es_archives/fleet/agents/mappings.json index 409cc3c689eaf..15e5a5524107b 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/mappings.json +++ b/x-pack/test/functional/es_archives/fleet/agents/mappings.json @@ -9,39 +9,42 @@ "dynamic": "strict", "_meta": { "migrationMappingPropertyHashes": { - "ingest-outputs": "aee9782e0d500b867859650a36280165", "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", "visualization": "52d7a13ad68a150c4525b292d23e12cc", "references": "7997cf5a56cc02bdc9c93361bde732b0", "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "epm-packages": "92b4b1899b887b090d01c033f3118a85", "type": "2f4316de49999235636386fe51dc06c1", - "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", + "ingest_manager_settings": "c5b0749b4ab03c582efd4c14cb8f132c", "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", "action": "6e96ac5e648f57523879661ea72525b7", - "agent_configs": "38abaf89513877745c359e7700c0c66a", "dashboard": "d00f614b29a80360e1190193fd333bab", - "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", - "siem-detection-engine-rule-actions": "90eee2e4635260f4be0a1da8f5bc0aa0", - "fleet-agent-events": "3231653fafe4ef3196fe3b32ab774bf2", + "metrics-explorer-view": "428e319af3e822c80a84cf87123ca35c", + "siem-detection-engine-rule-actions": "6569b288c169539db10cb262bf79de18", "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "fleet-agent-events": "3231653fafe4ef3196fe3b32ab774bf2", + "ingest-datasources": "2346514df03316001d56ed4c8d46fa94", "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "inventory-view": "9ecce5b58867403613d82fe496470b34", - "fleet-enrollment-api-keys": "28b91e20b105b6f928e2012600085d8f", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", + "inventory-view": "5299b67717e96502c77babf1c16fd4d3", + "upgrade-assistant-reindex-operation": "296a89039fc4260292be36b1b005d8f2", "cases-comments": "c2061fb929f585df57425102fa928b4b", + "fleet-enrollment-api-keys": "28b91e20b105b6f928e2012600085d8f", "canvas-element": "7390014e1091044523666d97247392fc", - "datasources": "d4bc0c252b2b5683ff21ea32d00acffc", + "ingest-outputs": "0e57221778a7153c8292edf154099036", "telemetry": "36a616f7026dfa617d6655df850fe16d", "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "namespaces": "2f4316de49999235636386fe51dc06c1", "server": "ec97f1c5da1a19609a60874e5af1100c", "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", "lens": "21c3ea0763beb1ecb0162529706b88c5", "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "fleet-agent-actions": "e520c855577170c24481be05c3ae14ec", "search": "181661168bbadd1eff5902361e2a0d5c", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", "cases-configure": "42711cbb311976c0687853f4c1354572", @@ -49,23 +52,22 @@ "alert": "7b44fba6773e37c806ce290ea9b7024e", "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", "map": "23d7aa4a720d4938ccde3983f87bd58d", - "uptime-dynamic-settings": "b6289473c8985c79b6c47eebc19a0ca5", - "epm-packages": "75d12cd13c867fd713d7dfb27366bc20", + "uptime-dynamic-settings": "fcdb453a30092f022f2642db29523d80", + "cases": "32aa96a6d3855ddda53010ae2048ac22", "apm-telemetry": "3525d7c22c42bc80f5e6e9cb3f2b26a2", - "cases": "08b8b110dbca273d37e8aef131ecab61", - "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", + "siem-ui-timeline": "f2d929253ecd06ffbac78b4047f45a86", "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "fleet-agents": "c3eeb7b9d97176f15f6d126370ab23c7", + "ingest-agent-configs": "f4bdc17427437537ca1754d5d5057ad5", + "url": "b675c3be8d76ecf029294d51dc7ec65d", "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "maps-telemetry": "268da3a48066123fc5baf35abaa55014", + "fleet-agents": "864760267df6c970f629bd4458506c53", + "maps-telemetry": "bfd39d88aadadb4be597ea984d433dbe", "namespace": "2f4316de49999235636386fe51dc06c1", "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "fleet-agent-actions": "ed270b46812f0fa1439366c428a2cf17", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "config": "ae24d22d5986d04124cc6568f771066f", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215" } @@ -107,145 +109,6 @@ } } }, - "fleet-agent-actions": { - "properties": { - "agent_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "data": { - "type": "flattened" - }, - "sent_at": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "agent_configs": { - "properties": { - "datasources": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "text" - }, - "namespace": { - "type": "keyword" - }, - "revision": { - "type": "integer" - }, - "status": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - }, - "updated_on": { - "type": "keyword" - } - } - }, - "fleet-agent-events": { - "properties": { - "action_id": { - "type": "keyword" - }, - "agent_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "data": { - "type": "text" - }, - "message": { - "type": "text" - }, - "payload": { - "type": "text" - }, - "stream_id": { - "type": "keyword" - }, - "subtype": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "fleet-agents": { - "properties": { - "access_api_key_id": { - "type": "keyword" - }, - "active": { - "type": "boolean" - }, - "config_id": { - "type": "keyword" - }, - "config_newest_revision": { - "type": "integer" - }, - "config_revision": { - "type": "integer" - }, - "current_error_events": { - "type": "text" - }, - "default_api_key": { - "type": "keyword" - }, - "enrolled_at": { - "type": "date" - }, - "last_checkin": { - "type": "date" - }, - "last_updated": { - "type": "date" - }, - "local_metadata": { - "type": "flattened" - }, - "shared_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "user_provided_metadata": { - "type": "flattened" - }, - "version": { - "type": "keyword" - } - } - }, "alert": { "properties": { "actions": { @@ -1355,6 +1218,9 @@ } } }, + "connector_id": { + "type": "keyword" + }, "created_at": { "type": "date" }, @@ -1630,137 +1496,180 @@ } } }, - "datasources": { + "epm-packages": { "properties": { - "config_id": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "enabled": { - "type": "boolean" + "es_index_patterns": { + "type": "object", + "dynamic": "false" }, - "inputs": { + "installed": { "type": "nested", "properties": { - "config": { - "type": "flattened" - }, - "enabled": { - "type": "boolean" - }, - "processors": { + "id": { "type": "keyword" }, - "streams": { - "type": "nested", - "properties": { - "config": { - "type": "flattened" - }, - "dataset": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "processors": { - "type": "keyword" - } - } - }, "type": { "type": "keyword" } } }, + "internal": { + "type": "boolean" + }, "name": { "type": "keyword" }, - "namespace": { - "type": "keyword" + "removable": { + "type": "boolean" }, - "output_id": { + "version": { "type": "keyword" - }, - "package": { - "properties": { - "name": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "revision": { - "type": "integer" } } }, - "fleet-enrollment-api-keys": { + "file-upload-telemetry": { "properties": { - "active": { - "type": "boolean" - }, - "api_key": { - "type": "binary" - }, - "api_key_id": { - "type": "keyword" - }, - "config_id": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "fleet-agent-actions": { + "properties": { + "agent_id": { "type": "keyword" }, "created_at": { "type": "date" }, - "expire_at": { - "type": "date" + "data": { + "type": "binary" }, - "name": { - "type": "keyword" + "sent_at": { + "type": "date" }, "type": { "type": "keyword" - }, - "updated_at": { - "type": "date" } } }, - "epm-packages": { + "fleet-agent-events": { "properties": { - "installed": { - "type": "nested", - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "name": { + "action_id": { + "type": "keyword" + }, + "agent_id": { + "type": "keyword" + }, + "config_id": { + "type": "keyword" + }, + "data": { + "type": "text" + }, + "message": { + "type": "text" + }, + "payload": { + "type": "text" + }, + "stream_id": { + "type": "keyword" + }, + "subtype": { + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agents": { + "properties": { + "access_api_key_id": { + "type": "keyword" + }, + "active": { + "type": "boolean" + }, + "config_id": { + "type": "keyword" + }, + "config_newest_revision": { + "type": "integer" + }, + "config_revision": { + "type": "integer" + }, + "current_error_events": { + "type": "text" + }, + "default_api_key": { + "type": "keyword" + }, + "default_api_key_id": { + "type": "keyword" + }, + "enrolled_at": { + "type": "date" + }, + "last_checkin": { + "type": "date" + }, + "last_updated": { + "type": "date" + }, + "local_metadata": { + "type": "flattened" + }, + "shared_id": { "type": "keyword" }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "user_provided_metadata": { + "type": "flattened" + }, "version": { "type": "keyword" } } }, - "file-upload-telemetry": { + "fleet-enrollment-api-keys": { "properties": { - "filesUploadedTotalCount": { - "type": "long" + "active": { + "type": "boolean" + }, + "api_key": { + "type": "binary" + }, + "api_key_id": { + "type": "keyword" + }, + "config_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "expire_at": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" } } }, @@ -1888,8 +1797,176 @@ } } }, + "ingest-agent-configs": { + "properties": { + "datasources": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "monitoring_enabled": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "namespace": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "updated_on": { + "type": "keyword" + } + } + }, + "ingest-datasources": { + "properties": { + "config_id": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "inputs": { + "type": "nested", + "properties": { + "config": { + "type": "flattened" + }, + "enabled": { + "type": "boolean" + }, + "processors": { + "type": "keyword" + }, + "streams": { + "type": "nested", + "properties": { + "agent_stream": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "dataset": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "processors": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + } + }, + "type": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + } + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "output_id": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "revision": { + "type": "integer" + } + } + }, + "ingest-outputs": { + "properties": { + "ca_sha256": { + "type": "keyword" + }, + "config": { + "type": "flattened" + }, + "fleet_enroll_password": { + "type": "binary" + }, + "fleet_enroll_username": { + "type": "binary" + }, + "hosts": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "ingest_manager_settings": { + "properties": { + "agent_auto_upgrade": { + "type": "keyword" + }, + "kibana_ca_sha256": { + "type": "keyword" + }, + "kibana_url": { + "type": "keyword" + }, + "package_auto_upgrade": { + "type": "keyword" + } + } + }, "inventory-view": { "properties": { + "accountId": { + "type": "keyword" + }, "autoBounds": { "type": "boolean" }, @@ -1983,8 +2060,11 @@ "nodeType": { "type": "keyword" }, + "region": { + "type": "keyword" + }, "time": { - "type": "integer" + "type": "long" }, "view": { "type": "keyword" @@ -2102,6 +2182,12 @@ "indexPatternsWithGeoFieldCount": { "type": "long" }, + "indexPatternsWithGeoPointFieldCount": { + "type": "long" + }, + "indexPatternsWithGeoShapeFieldCount": { + "type": "long" + }, "mapsTotalCount": { "type": "long" }, @@ -2156,6 +2242,9 @@ "filterQuery": { "type": "keyword" }, + "forceInterval": { + "type": "boolean" + }, "groupBy": { "type": "keyword" }, @@ -2211,36 +2300,8 @@ "namespace": { "type": "keyword" }, - "ingest-outputs": { - "properties": { - "api_key": { - "type": "keyword" - }, - "ca_sha256": { - "type": "keyword" - }, - "config": { - "type": "flattened" - }, - "fleet_enroll_password": { - "type": "binary" - }, - "fleet_enroll_username": { - "type": "binary" - }, - "hosts": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } + "namespaces": { + "type": "keyword" }, "query": { "properties": { @@ -2346,7 +2407,7 @@ }, "params": { "type": "object", - "dynamic": "true" + "enabled": false } } }, @@ -2647,6 +2708,15 @@ } } }, + "templateTimelineId": { + "type": "text" + }, + "templateTimelineVersion": { + "type": "integer" + }, + "timelineType": { + "type": "keyword" + }, "title": { "type": "text" }, @@ -2827,11 +2897,48 @@ "type": "date" }, "upgrade-assistant-reindex-operation": { - "dynamic": "true", "properties": { + "errorMessage": { + "type": "keyword" + }, "indexName": { "type": "keyword" }, + "lastCompletedStep": { + "type": "integer" + }, + "locked": { + "type": "date" + }, + "newIndexName": { + "type": "keyword" + }, + "reindexOptions": { + "properties": { + "openAndClose": { + "type": "boolean" + }, + "queueSettings": { + "properties": { + "queuedAt": { + "type": "long" + }, + "startedAt": { + "type": "long" + } + } + } + } + }, + "reindexTaskId": { + "type": "keyword" + }, + "reindexTaskPercComplete": { + "type": "float" + }, + "runningReindexCount": { + "type": "integer" + }, "status": { "type": "integer" } @@ -2891,6 +2998,12 @@ }, "uptime-dynamic-settings": { "properties": { + "certAgeThreshold": { + "type": "long" + }, + "certExpirationThreshold": { + "type": "long" + }, "heartbeatIndices": { "type": "keyword" } @@ -2911,8 +3024,7 @@ "type": "text", "fields": { "keyword": { - "type": "keyword", - "ignore_above": 2048 + "type": "keyword" } } }