diff --git a/src/core/server/saved_objects/serialization/types.ts b/src/core/server/saved_objects/serialization/types.ts index 524c2c8ffae7ae..dfaec127ba1593 100644 --- a/src/core/server/saved_objects/serialization/types.ts +++ b/src/core/server/saved_objects/serialization/types.ts @@ -50,7 +50,7 @@ export interface SavedObjectsRawDocSource { * scenario out of the box. */ interface SavedObjectDoc { - attributes: unknown; + attributes: any; id?: string; // NOTE: SavedObjectDoc is used for uncreated objects where `id` is optional type: string; namespace?: string; diff --git a/src/legacy/core_plugins/data/index.ts b/src/legacy/core_plugins/data/index.ts index 813eab00f72582..10c8cf464b82de 100644 --- a/src/legacy/core_plugins/data/index.ts +++ b/src/legacy/core_plugins/data/index.ts @@ -19,8 +19,6 @@ import { resolve } from 'path'; import { Legacy } from '../../../../kibana'; -import { mappings } from './mappings'; -import { SavedQuery } from '../../../plugins/data/public'; // eslint-disable-next-line import/no-default-export export default function DataPlugin(kibana: any) { @@ -36,23 +34,6 @@ export default function DataPlugin(kibana: any) { init: (server: Legacy.Server) => ({}), uiExports: { injectDefaultVars: () => ({}), - mappings, - savedObjectsManagement: { - query: { - icon: 'search', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj: SavedQuery) { - return obj.attributes.title; - }, - getInAppUrl(obj: SavedQuery) { - return { - path: `/app/kibana#/discover?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`, - uiCapabilitiesPath: 'discover.show', - }; - }, - }, - }, }, }; diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 092eed924f3306..1d772536fa1ea2 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -126,57 +126,6 @@ export default function(kibana) { ], savedObjectsManagement: { - 'index-pattern': { - icon: 'indexPatternApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'management.kibana.index_patterns', - }; - }, - }, - visualization: { - icon: 'visualizeApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/objects/savedVisualizations/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/visualize/edit/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'visualize.show', - }; - }, - }, - search: { - icon: 'discoverApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/objects/savedSearches/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/discover/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'discover.show', - }; - }, - }, dashboard: { icon: 'dashboardApp', defaultSearchField: 'title', diff --git a/src/legacy/core_plugins/kibana/mappings.json b/src/legacy/core_plugins/kibana/mappings.json index 4cf9ea1d301c09..af3f79588552b6 100644 --- a/src/legacy/core_plugins/kibana/mappings.json +++ b/src/legacy/core_plugins/kibana/mappings.json @@ -1,93 +1,4 @@ { - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "dashboard": { "properties": { "description": { diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.js b/src/legacy/core_plugins/kibana/migrations/migrations.js index 29b6e632d19fd1..d37887c640b90b 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.js @@ -17,7 +17,7 @@ * under the License. */ -import { cloneDeep, get, omit, has, flow } from 'lodash'; +import { get } from 'lodash'; import { migrations730 as dashboardMigrations730 } from '../public/dashboard/migrations'; function migrateIndexPattern(doc) { @@ -58,559 +58,7 @@ function migrateIndexPattern(doc) { doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); } -// [TSVB] Migrate percentile-rank aggregation (value -> values) -const migratePercentileRankAggregation = doc => { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState && visState.type === 'metrics') { - const series = get(visState, 'params.series') || []; - - series.forEach(part => { - (part.metrics || []).forEach(metric => { - if (metric.type === 'percentile_rank' && has(metric, 'value')) { - metric.values = [metric.value]; - - delete metric.value; - } - }); - }); - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } - return doc; -}; - -// Migrate date histogram aggregation (remove customInterval) -const migrateDateHistogramAggregation = doc => { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type === 'date_histogram' && agg.params) { - if (agg.params.interval === 'custom') { - agg.params.interval = agg.params.customInterval; - } - delete agg.params.customInterval; - } - - if ( - get(agg, 'params.customBucket.type', null) === 'date_histogram' && - agg.params.customBucket.params - ) { - if (agg.params.customBucket.params.interval === 'custom') { - agg.params.customBucket.params.interval = agg.params.customBucket.params.customInterval; - } - delete agg.params.customBucket.params.customInterval; - } - }); - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } - return doc; -}; - -function removeDateHistogramTimeZones(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - // We're checking always for the existance of agg.params here. This should always exist, but better - // be safe then sorry during migrations. - if (agg.type === 'date_histogram' && agg.params) { - delete agg.params.time_zone; - } - - if ( - get(agg, 'params.customBucket.type', null) === 'date_histogram' && - agg.params.customBucket.params - ) { - delete agg.params.customBucket.params.time_zone; - } - }); - doc.attributes.visState = JSON.stringify(visState); - } - } - return doc; -} - -// migrate gauge verticalSplit to alignment -// https://github.com/elastic/kibana/issues/34636 -function migrateGaugeVerticalSplitToAlignment(doc, logger) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.type === 'gauge' && !visState.params.gauge.alignment) { - visState.params.gauge.alignment = visState.params.gauge.verticalSplit - ? 'vertical' - : 'horizontal'; - delete visState.params.gauge.verticalSplit; - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - logger.warning(`Exception @ migrateGaugeVerticalSplitToAlignment! ${e}`); - logger.warning(`Exception @ migrateGaugeVerticalSplitToAlignment! Payload: ${visStateJSON}`); - } - } - return doc; -} -// Migrate filters (string -> { query: string, language: lucene }) -/* - Enabling KQL in TSVB causes problems with savedObject visualizations when these are saved with filters. - In a visualisation type of saved object, if the visState param is of type metric, the filter is saved as a string that is not interpretted correctly as a lucene query in the visualization itself. - We need to transform the filter string into an object containing the original string as a query and specify the query language as lucene. - For Metrics visualizations (param.type === "metric"), filters can be applied to each series object in the series array within the SavedObject.visState.params object. - Path to the series array is thus: - attributes.visState. -*/ -function transformFilterStringToQueryObject(doc) { - // Migrate filters - // If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly - const newDoc = cloneDeep(doc); - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const visType = get(visState, 'params.type'); - const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; - if (tsvbTypes.indexOf(visType) === -1) { - // skip - return doc; - } - // migrate the params fitler - const params = get(visState, 'params'); - if (params.filter && typeof params.filter === 'string') { - const paramsFilterObject = { - query: params.filter, - language: 'lucene', - }; - params.filter = paramsFilterObject; - } - - // migrate the annotations query string: - const annotations = get(visState, 'params.annotations') || []; - annotations.forEach(item => { - if (!item.query_string) { - // we don't need to transform anything if there isn't a filter at all - return; - } - if (typeof item.query_string === 'string') { - const itemQueryStringObject = { - query: item.query_string, - language: 'lucene', - }; - item.query_string = itemQueryStringObject; - } - }); - // migrate the series filters - const series = get(visState, 'params.series') || []; - series.forEach(item => { - if (!item.filter) { - // we don't need to transform anything if there isn't a filter at all - return; - } - // series item filter - if (typeof item.filter === 'string') { - const itemfilterObject = { - query: item.filter, - language: 'lucene', - }; - item.filter = itemfilterObject; - } - // series item split filters filter - if (item.split_filters) { - const splitFilters = get(item, 'split_filters') || []; - splitFilters.forEach(filter => { - if (!filter.filter) { - // we don't need to transform anything if there isn't a filter at all - return; - } - if (typeof filter.filter === 'string') { - const filterfilterObject = { - query: filter.filter, - language: 'lucene', - }; - filter.filter = filterfilterObject; - } - }); - } - }); - newDoc.attributes.visState = JSON.stringify(visState); - } - } - return newDoc; -} -function transformSplitFiltersStringToQueryObject(doc) { - // Migrate split_filters in TSVB objects that weren't migrated in 7.3 - // If any filters exist and they are a string, we assume them to be lucene syntax and transform the filter into an object accordingly - const newDoc = cloneDeep(doc); - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const visType = get(visState, 'params.type'); - const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; - if (tsvbTypes.indexOf(visType) === -1) { - // skip - return doc; - } - // migrate the series split_filter filters - const series = get(visState, 'params.series') || []; - series.forEach(item => { - // series item split filters filter - if (item.split_filters) { - const splitFilters = get(item, 'split_filters') || []; - if (splitFilters.length > 0) { - // only transform split_filter filters if we have filters - splitFilters.forEach(filter => { - if (typeof filter.filter === 'string') { - const filterfilterObject = { - query: filter.filter, - language: 'lucene', - }; - filter.filter = filterfilterObject; - } - }); - } - } - }); - newDoc.attributes.visState = JSON.stringify(visState); - } - } - return newDoc; -} - -function migrateFiltersAggQuery(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type !== 'filters') return; - - agg.params.filters.forEach(filter => { - if (filter.input.language) return filter; - filter.input.language = 'lucene'; - }); - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - } - return doc; -} - -function replaceMovAvgToMovFn(doc, logger) { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - - if (visState && visState.type === 'metrics') { - const series = get(visState, 'params.series', []); - - series.forEach(part => { - if (part.metrics && Array.isArray(part.metrics)) { - part.metrics.forEach(metric => { - if (metric.type === 'moving_average') { - metric.model_type = metric.model; - metric.alpha = get(metric, 'settings.alpha', 0.3); - metric.beta = get(metric, 'settings.beta', 0.1); - metric.gamma = get(metric, 'settings.gamma', 0.3); - metric.period = get(metric, 'settings.period', 1); - metric.multiplicative = get(metric, 'settings.type') === 'mult'; - - delete metric.minimize; - delete metric.model; - delete metric.settings; - delete metric.predict; - } - }); - } - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - logger.warning(`Exception @ replaceMovAvgToMovFn! ${e}`); - logger.warning(`Exception @ replaceMovAvgToMovFn! Payload: ${visStateJSON}`); - } - } - - return doc; -} - -function migrateSearchSortToNestedArray(doc) { - const sort = get(doc, 'attributes.sort'); - if (!sort) return doc; - - // Don't do anything if we already have a two dimensional array - if (Array.isArray(sort) && sort.length > 0 && Array.isArray(sort[0])) { - return doc; - } - - return { - ...doc, - attributes: { - ...doc.attributes, - sort: [doc.attributes.sort], - }, - }; -} - -function migrateFiltersAggQueryStringQueries(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type !== 'filters') return doc; - - agg.params.filters.forEach(filter => { - if (filter.input.query.query_string) { - filter.input.query = filter.input.query.query_string.query; - } - }); - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - } - return doc; -} - -function migrateSubTypeAndParentFieldProperties(doc) { - if (!doc.attributes.fields) return doc; - - const fieldsString = doc.attributes.fields; - const fields = JSON.parse(fieldsString); - const migratedFields = fields.map(field => { - if (field.subType === 'multi') { - return { - ...omit(field, 'parent'), - subType: { multi: { parent: field.parent } }, - }; - } - - return field; - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - fields: JSON.stringify(migratedFields), - }, - }; -} - -const executeMigrations720 = flow( - migratePercentileRankAggregation, - migrateDateHistogramAggregation -); -const executeMigrations730 = flow( - migrateGaugeVerticalSplitToAlignment, - transformFilterStringToQueryObject, - migrateFiltersAggQuery, - replaceMovAvgToMovFn -); - -const executeVisualizationMigrations731 = flow(migrateFiltersAggQueryStringQueries); - -const executeSearchMigrations740 = flow(migrateSearchSortToNestedArray); - -const executeMigrations742 = flow(transformSplitFiltersStringToQueryObject); - export const migrations = { - 'index-pattern': { - '6.5.0': doc => { - doc.attributes.type = doc.attributes.type || undefined; - doc.attributes.typeMeta = doc.attributes.typeMeta || undefined; - return doc; - }, - '7.6.0': flow(migrateSubTypeAndParentFieldProperties), - }, - visualization: { - /** - * We need to have this migration twice, once with a version prior to 7.0.0 once with a version - * after it. The reason for that is, that this migration has been introduced once 7.0.0 was already - * released. Thus a user who already had 7.0.0 installed already got the 7.0.0 migrations below running, - * so we need a version higher than that. But this fix was backported to the 6.7 release, meaning if we - * would only have the 7.0.1 migration in here a user on the 6.7 release will migrate their saved objects - * to the 7.0.1 state, and thus when updating their Kibana to 7.0, will never run the 7.0.0 migrations introduced - * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 - * only contained the 6.7.2 migration and not the 7.0.1 migration. - */ - '6.7.2': removeDateHistogramTimeZones, - '7.0.0': doc => { - // Set new "references" attribute - doc.references = doc.references || []; - - // Migrate index pattern - migrateIndexPattern(doc); - - // Migrate saved search - const savedSearchId = get(doc, 'attributes.savedSearchId'); - if (savedSearchId) { - doc.references.push({ - type: 'search', - name: 'search_0', - id: savedSearchId, - }); - doc.attributes.savedSearchRefName = 'search_0'; - } - delete doc.attributes.savedSearchId; - - // Migrate controls - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const controls = get(visState, 'params.controls') || []; - controls.forEach((control, i) => { - if (!control.indexPattern) { - return; - } - control.indexPatternRefName = `control_${i}_index_pattern`; - doc.references.push({ - name: control.indexPatternRefName, - type: 'index-pattern', - id: control.indexPattern, - }); - delete control.indexPattern; - }); - doc.attributes.visState = JSON.stringify(visState); - } - } - - // Migrate table splits - try { - const visState = JSON.parse(doc.attributes.visState); - if (get(visState, 'type') !== 'table') { - return doc; // do nothing; we only want to touch tables - } - - let splitCount = 0; - visState.aggs = visState.aggs.map(agg => { - if (agg.schema !== 'split') { - return agg; - } - - splitCount++; - if (splitCount === 1) { - return agg; // leave the first split agg unchanged - } - agg.schema = 'bucket'; - // the `row` param is exclusively used by split aggs, so we remove it - agg.params = omit(agg.params, ['row']); - return agg; - }); - - if (splitCount <= 1) { - return doc; // do nothing; we only want to touch tables with multiple split aggs - } - - const newDoc = cloneDeep(doc); - newDoc.attributes.visState = JSON.stringify(visState); - return newDoc; - } catch (e) { - throw new Error( - `Failure attempting to migrate saved object '${doc.attributes.title}' - ${e}` - ); - } - }, - '7.0.1': removeDateHistogramTimeZones, - '7.2.0': doc => executeMigrations720(doc), - '7.3.0': executeMigrations730, - '7.3.1': executeVisualizationMigrations731, - // migrate split_filters that were not migrated in 7.3.0 (transformFilterStringToQueryObject). - '7.4.2': executeMigrations742, - }, dashboard: { '7.0.0': doc => { // Set new "references" attribute @@ -651,14 +99,4 @@ export const migrations = { }, '7.3.0': dashboardMigrations730, }, - search: { - '7.0.0': doc => { - // Set new "references" attribute - doc.references = doc.references || []; - // Migrate index pattern - migrateIndexPattern(doc); - return doc; - }, - '7.4.0': executeSearchMigrations740, - }, }; diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.test.js b/src/legacy/core_plugins/kibana/migrations/migrations.test.js index e39bc59201e7fd..b02081128c8585 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.test.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.test.js @@ -19,1312 +19,6 @@ import { migrations } from './migrations'; -describe('index-pattern', () => { - describe('6.5.0', () => { - const migrate = doc => migrations['index-pattern']['6.5.0'](doc); - - it('adds "type" and "typeMeta" properties to object when not declared', () => { - expect( - migrate({ - attributes: {}, - }) - ).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "type": undefined, - "typeMeta": undefined, - }, -} -`); - }); - - it('keeps "type" and "typeMeta" properties as is when declared', () => { - expect( - migrate({ - attributes: { - type: '123', - typeMeta: '123', - }, - }) - ).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "type": "123", - "typeMeta": "123", - }, -} -`); - }); - }); - - describe('7.6.0', function() { - const migrate = doc => migrations['index-pattern']['7.6.0'](doc); - - it('should remove the parent property and update the subType prop on every field that has them', () => { - const input = { - attributes: { - title: 'test', - fields: - '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":"multi","parent":"customer_name"}]', - }, - }; - const expected = { - attributes: { - title: 'test', - fields: - '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"customer_name"}}}]', - }, - }; - - expect(migrate(input)).toEqual(expected); - }); - }); -}); - -describe('visualization', () => { - describe('date histogram time zone removal', () => { - const migrate = doc => migrations.visualization['6.7.2'](doc); - let doc; - beforeEach(() => { - doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - enabled: true, - id: '1', - params: { - // Doesn't make much sense but we want to test it's not removing it from anything else - time_zone: 'Europe/Berlin', - }, - schema: 'metric', - type: 'count', - }, - { - enabled: true, - id: '2', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - time_zone: 'Europe/Berlin', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '4', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '3', - params: { - customBucket: { - enabled: true, - id: '1-bucket', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - time_zone: 'Europe/Berlin', - useNormalizedEsInterval: true, - }, - type: 'date_histogram', - }, - customMetric: { - enabled: true, - id: '1-metric', - params: {}, - type: 'count', - }, - }, - schema: 'metric', - type: 'max_bucket', - }, - ], - }), - }, - }; - }); - - it('should remove time_zone from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.time_zone'); - }); - - it('should not remove time_zone from non date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[0]).toHaveProperty('params.time_zone'); - }); - - it('should remove time_zone from nested aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); - }); - - it('should not fail on date histograms without a time_zone', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[2]).not.toHaveProperty('params.time_zone'); - }); - - it('should be able to apply the migration twice, since we need it for 6.7.2 and 7.0.1', () => { - const migratedDoc = migrate(migrate(doc)); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.time_zone'); - expect(aggs[0]).toHaveProperty('params.time_zone'); - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); - expect(aggs[2]).not.toHaveProperty('params.time_zone'); - }); - }); - - describe('7.0.0', () => { - const migrate = doc => migrations.visualization['7.0.0'](doc); - const generateDoc = ({ type, aggs }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ type, aggs }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - - it('does not throw error on empty object', () => { - const migratedDoc = migrate({ - attributes: { - visState: '{}', - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{}", - }, - "references": Array [], -} -`); - }); - - it('skips errors when searchSourceJSON is null', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: null, - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": null, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips errors when searchSourceJSON is undefined', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: undefined, - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": undefined, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when searchSourceJSON is not a string', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: 123, - }, - savedSearchId: '123', - }, - }; - expect(migrate(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": 123, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when searchSourceJSON is invalid json', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{abc123}', - }, - savedSearchId: '123', - }, - }; - expect(migrate(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{abc123}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when "index" and "filter" is missing from searchSourceJSON', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts "index" attribute from doc', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "pattern*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern", - }, - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts index patterns from the filter', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - bar: true, - filter: [ - { - meta: { index: 'my-index', foo: true }, - }, - ], - }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "my-index", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern", - }, - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts index patterns from controls', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - foo: true, - visState: JSON.stringify({ - bar: false, - params: { - controls: [ - { - bar: true, - indexPattern: 'pattern*', - }, - { - foo: true, - }, - ], - }, - }), - }, - }; - const migratedDoc = migrate(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "visState": "{\\"bar\\":false,\\"params\\":{\\"controls\\":[{\\"bar\\":true,\\"indexPatternRefName\\":\\"control_0_index_pattern\\"},{\\"foo\\":true}]}}", - }, - "id": "1", - "references": Array [ - Object { - "id": "pattern*", - "name": "control_0_index_pattern", - "type": "index-pattern", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips extracting savedSearchId when missing', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "visState": "{}", - }, - "id": "1", - "references": Array [], -} -`); - }); - - it('extract savedSearchId from doc', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], -} -`); - }); - - it('delete savedSearchId when empty string in doc', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - savedSearchId: '', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "visState": "{}", - }, - "id": "1", - "references": Array [], -} -`); - }); - - it('should return a new object if vis is table and has multiple split aggs', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - ]; - const tableDoc = generateDoc({ type: 'table', aggs }); - const expected = tableDoc; - const actual = migrate(tableDoc); - expect(actual).not.toBe(expected); - }); - - it('should not touch any vis that is not table', () => { - const aggs = []; - const pieDoc = generateDoc({ type: 'pie', aggs }); - const expected = pieDoc; - const actual = migrate(pieDoc); - expect(actual).toBe(expected); - }); - - it('should not change values in any vis that is not table', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'segment', - params: { hey: 'ya' }, - }, - ]; - const pieDoc = generateDoc({ type: 'pie', aggs }); - const expected = pieDoc; - const actual = migrate(pieDoc); - expect(actual).toEqual(expected); - }); - - it('should not touch table vis if there are not multiple split aggs', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - ]; - const tableDoc = generateDoc({ type: 'table', aggs }); - const expected = tableDoc; - const actual = migrate(tableDoc); - expect(actual).toBe(expected); - }); - - it('should change all split aggs to `bucket` except the first', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - { - id: '4', - schema: 'bucket', - params: { heyyy: 'yaaa' }, - }, - ]; - const expected = ['metric', 'split', 'bucket', 'bucket']; - const migrated = migrate(generateDoc({ type: 'table', aggs })); - const actual = JSON.parse(migrated.attributes.visState); - expect(actual.aggs.map(agg => agg.schema)).toEqual(expected); - }); - - it('should remove `rows` param from any aggs that are not `split`', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - ]; - const expected = [{}, { foo: 'bar', row: true }, { hey: 'ya' }]; - const migrated = migrate(generateDoc({ type: 'table', aggs })); - const actual = JSON.parse(migrated.attributes.visState); - expect(actual.aggs.map(agg => agg.params)).toEqual(expected); - }); - - it('should throw with a reference to the doc name if something goes wrong', () => { - const doc = { - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: '!/// Intentionally malformed JSON ///!', - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }; - expect(() => migrate(doc)).toThrowError(/My Vis/); - }); - }); - - describe('date histogram custom interval removal', () => { - const migrate = doc => migrations.visualization['7.2.0'](doc); - let doc; - beforeEach(() => { - doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - enabled: true, - id: '1', - params: { - customInterval: '1h', - }, - schema: 'metric', - type: 'count', - }, - { - enabled: true, - id: '2', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '4', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'custom', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '3', - params: { - customBucket: { - enabled: true, - id: '1-bucket', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'custom', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - type: 'date_histogram', - }, - customMetric: { - enabled: true, - id: '1-metric', - params: {}, - type: 'count', - }, - }, - schema: 'metric', - type: 'max_bucket', - }, - ], - }), - }, - }; - }); - - it('should remove customInterval from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.customInterval'); - }); - - it('should not change interval from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1].params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[1].params.interval - ); - }); - - it('should not remove customInterval from non date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[0]).toHaveProperty('params.customInterval'); - }); - - it('should set interval with customInterval value and remove customInterval when interval equals "custom"', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[2].params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[2].params.customInterval - ); - expect(aggs[2]).not.toHaveProperty('params.customInterval'); - }); - - it('should remove customInterval from nested aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); - }); - - it('should remove customInterval from nested aggregations and set interval with customInterval value', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3].params.customBucket.params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[3].params.customBucket.params.customInterval - ); - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); - }); - - it('should not fail on date histograms without a customInterval', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customInterval'); - }); - }); - describe('7.3.0', () => { - const logMsgArr = []; - const logger = { - warning: msg => logMsgArr.push(msg), - }; - const migrate = doc => migrations.visualization['7.3.0'](doc, logger); - - it('migrates type = gauge verticalSplit: false to alignment: vertical', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: false } } }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"horizontal\\"}}}", - }, -} -`); - }); - - it('migrates type = gauge verticalSplit: false to alignment: horizontal', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: true } } }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"vertical\\"}}}", - }, -} -`); - }); - - it('doesnt migrate type = gauge containing invalid visState object, adds message to log', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge' }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\"}", - }, -} -`); - expect(logMsgArr).toMatchInlineSnapshot(` -Array [ - "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", - "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", -] -`); - }); - - describe('filters agg query migration', () => { - const doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - type: 'filters', - params: { - filters: [ - { - input: { - query: 'response:200', - }, - label: '', - }, - { - input: { - query: 'response:404', - }, - label: 'bad response', - }, - { - input: { - query: { - exists: { - field: 'phpmemory', - }, - }, - }, - label: '', - }, - ], - }, - }, - ], - }), - }, - }; - - it('should add language property to filters without one, assuming lucene', () => { - const migrationResult = migrate(doc); - expect(migrationResult).toEqual({ - attributes: { - visState: JSON.stringify({ - aggs: [ - { - type: 'filters', - params: { - filters: [ - { - input: { - query: 'response:200', - language: 'lucene', - }, - label: '', - }, - { - input: { - query: 'response:404', - language: 'lucene', - }, - label: 'bad response', - }, - { - input: { - query: { - exists: { - field: 'phpmemory', - }, - }, - language: 'lucene', - }, - label: '', - }, - ], - }, - }, - ], - }), - }, - }); - }); - }); - - describe('replaceMovAvgToMovFn()', () => { - let doc; - - beforeEach(() => { - doc = { - attributes: { - title: 'VIS', - visState: `{"title":"VIS","type":"metrics","params":{"id":"61ca57f0-469d-11e7-af02-69e470af7417", - "type":"timeseries","series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","color":"rgba(0,156,224,1)", - "split_mode":"terms","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417","type":"count", - "numerator":"FlightDelay:true"},{"settings":"","minimize":0,"window":5,"model": - "holt_winters","id":"23054fe0-8915-11e9-9b86-d3f94982620f","type":"moving_average","field": - "61ca57f2-469d-11e7-af02-69e470af7417","predict":1}],"separate_axis":0,"axis_position":"right", - "formatter":"number","chart_type":"line","line_width":"2","point_size":"0","fill":0.5,"stacked":"none", - "label":"Percent Delays","terms_size":"2","terms_field":"OriginCityName"}],"time_field":"timestamp", - "index_pattern":"kibana_sample_data_flights","interval":">=12h","axis_position":"left","axis_formatter": - "number","show_legend":1,"show_grid":1,"annotations":[{"fields":"FlightDelay,Cancelled,Carrier", - "template":"{{Carrier}}: Flight Delayed and Cancelled!","index_pattern":"kibana_sample_data_flights", - "query_string":"FlightDelay:true AND Cancelled:true","id":"53b7dff0-4c89-11e8-a66a-6989ad5a0a39", - "color":"rgba(0,98,177,1)","time_field":"timestamp","icon":"fa-exclamation-triangle", - "ignore_global_filters":1,"ignore_panel_filters":1,"hidden":true}],"legend_position":"bottom", - "axis_scale":"normal","default_index_pattern":"kibana_sample_data_flights","default_timefield":"timestamp"}, - "aggs":[]}`, - }, - migrationVersion: { - visualization: '7.2.0', - }, - type: 'visualization', - }; - }); - - test('should add some necessary moving_fn fields', () => { - const migratedDoc = migrate(doc); - const visState = JSON.parse(migratedDoc.attributes.visState); - const metric = visState.params.series[0].metrics[1]; - - expect(metric).toHaveProperty('model_type'); - expect(metric).toHaveProperty('alpha'); - expect(metric).toHaveProperty('beta'); - expect(metric).toHaveProperty('gamma'); - expect(metric).toHaveProperty('period'); - expect(metric).toHaveProperty('multiplicative'); - }); - }); - }); - describe('7.3.0 tsvb', () => { - const migrate = doc => migrations.visualization['7.3.0'](doc); - const generateDoc = ({ params }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ params }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - it('should change series item filters from a string into an object', () => { - const params = { type: 'metric', series: [{ filter: 'Filter Bytes Test:>1000' }] }; - const testDoc1 = generateDoc({ params }); - const migratedTestDoc1 = migrate(testDoc1); - const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; - expect(series[0].filter).toHaveProperty('query'); - expect(series[0].filter).toHaveProperty('language'); - }); - it('should not change a series item filter string in the object after migration', () => { - const markdownParams = { - type: 'markdown', - series: [ - { - filter: 'Filter Bytes Test:>1000', - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - }; - const markdownDoc = generateDoc({ params: markdownParams }); - const migratedMarkdownDoc = migrate(markdownDoc); - const markdownSeries = JSON.parse(migratedMarkdownDoc.attributes.visState).params.series; - expect(markdownSeries[0].filter.query).toBe( - JSON.parse(markdownDoc.attributes.visState).params.series[0].filter - ); - expect(markdownSeries[0].split_filters[0].filter.query).toBe( - JSON.parse(markdownDoc.attributes.visState).params.series[0].split_filters[0].filter - ); - }); - it('should change series item filters from a string into an object for all filters', () => { - const params = { - type: 'timeseries', - filter: 'bytes:>1000', - series: [ - { - filter: 'Filter Bytes Test:>1000', - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - annotations: [{ query_string: 'bytes:>1000' }], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(Object.keys(timeSeriesParams.series[0].filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(Object.keys(timeSeriesParams.series[0].split_filters[0].filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(Object.keys(timeSeriesParams.annotations[0].query_string)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - }); - it('should not fail on a metric visualization without a filter in a series item', () => { - const params = { type: 'metric', series: [{}, {}, {}] }; - const testDoc1 = generateDoc({ params }); - const migratedTestDoc1 = migrate(testDoc1); - const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; - expect(series[2]).not.toHaveProperty('filter.query'); - }); - it('should not migrate a visualization of unknown type', () => { - const params = { type: 'unknown', series: [{ filter: 'foo:bar' }] }; - const doc = generateDoc({ params }); - const migratedDoc = migrate(doc); - const series = JSON.parse(migratedDoc.attributes.visState).params.series; - expect(series[0].filter).toEqual(params.series[0].filter); - }); - }); - - describe('7.3.1', () => { - const migrate = migrations.visualization['7.3.1']; - - it('should migrate filters agg query string queries', () => { - const state = { - aggs: [ - { type: 'count', params: {} }, - { - type: 'filters', - params: { - filters: [ - { - input: { - query: { - query_string: { query: 'machine.os.keyword:"win 8"' }, - }, - }, - }, - ], - }, - }, - ], - }; - const expected = { - aggs: [ - { type: 'count', params: {} }, - { - type: 'filters', - params: { - filters: [{ input: { query: 'machine.os.keyword:"win 8"' } }], - }, - }, - ], - }; - const migratedDoc = migrate({ attributes: { visState: JSON.stringify(state) } }); - expect(migratedDoc).toEqual({ attributes: { visState: JSON.stringify(expected) } }); - }); - }); - describe('7.4.2 tsvb split_filters migration', () => { - const migrate = doc => migrations.visualization['7.4.2'](doc); - const generateDoc = ({ params }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ params }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - it('should change series item filters from a string into an object for all filters', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(Object.keys(timeSeriesParams.filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ - query: 'bytes:>1000', - language: 'lucene', - }); - }); - it('should change series item split filters when there is no filter item', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - annotations: [ - { - query_string: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ - query: 'bytes:>1000', - language: 'lucene', - }); - }); - it('should not convert split_filters to objects if there are no split filter filters', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [], - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters).not.toHaveProperty('query'); - }); - it('should do nothing if a split_filter is already a query:language object', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [ - { - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }, - ], - annotations: [ - { - query_string: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters[0].filter.query).toEqual('bytes:>1000'); - expect(timeSeriesParams.series[0].split_filters[0].filter.language).toEqual('lucene'); - }); - }); -}); - describe('dashboard', () => { describe('7.0.0', () => { const migration = migrations.dashboard['7.0.0']; @@ -1751,271 +445,3 @@ Object { }); }); }); - -describe('search', () => { - describe('7.0.0', () => { - const migration = migrations.search['7.0.0']; - - test('skips errors when searchSourceJSON is null', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: null, - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": null, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips errors when searchSourceJSON is undefined', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: undefined, - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": undefined, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when searchSourceJSON is not a string', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: 123, - }, - }, - }; - expect(migration(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": 123, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when searchSourceJSON is invalid json', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: '{abc123}', - }, - }, - }; - expect(migration(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{abc123}", - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when "index" and "filter" is missing from searchSourceJSON', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true }), - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true}", - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('extracts "index" attribute from doc', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", - }, - }, - "id": "123", - "references": Array [ - Object { - "id": "pattern*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern", - }, - ], - "type": "search", -} -`); - }); - - test('extracts index patterns from filter', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - bar: true, - filter: [ - { - meta: { - foo: true, - index: 'my-index', - }, - }, - ], - }), - }, - }, - }; - const migratedDoc = migration(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", - }, - }, - "id": "123", - "references": Array [ - Object { - "id": "my-index", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern", - }, - ], - "type": "search", -} -`); - }); - }); - - describe('7.4.0', function() { - const migration = migrations.search['7.4.0']; - - test('transforms one dimensional sort arrays into two dimensional arrays', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - sort: ['bytes', 'desc'], - }, - }; - - const expected = { - id: '123', - type: 'search', - attributes: { - sort: [['bytes', 'desc']], - }, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(expected); - }); - - test("doesn't modify search docs that already have two dimensional sort arrays", () => { - const doc = { - id: '123', - type: 'search', - attributes: { - sort: [['bytes', 'desc']], - }, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(doc); - }); - - test("doesn't modify search docs that have no sort array", () => { - const doc = { - id: '123', - type: 'search', - attributes: {}, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(doc); - }); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable.ts index 91726c69189f36..d09b7612af49c6 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable.ts @@ -20,7 +20,10 @@ import _ from 'lodash'; import * as Rx from 'rxjs'; import { Subscription } from 'rxjs'; import { i18n } from '@kbn/i18n'; -import { UiActionsStart } from 'src/plugins/ui_actions/public'; +import { + UiActionsStart, + APPLY_FILTER_TRIGGER, +} from '../../../../../../..//plugins/ui_actions/public'; import { RequestAdapter, Adapters } from '../../../../../../../plugins/inspector/public'; import { esFilters, @@ -31,11 +34,7 @@ import { Query, IFieldType, } from '../../../../../../../plugins/data/public'; -import { - APPLY_FILTER_TRIGGER, - Container, - Embeddable, -} from '../../../../../embeddable_api/public/np_ready/public'; +import { Container, Embeddable } from '../../../../../embeddable_api/public/np_ready/public'; import * as columnActions from '../angular/doc_table/actions/columns'; import searchTemplate from './search_template.html'; import { ISearchEmbeddable, SearchInput, SearchOutput } from './types'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts index 0543dc949bbd2a..474912ed508f83 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts @@ -34,10 +34,12 @@ import { EmbeddableOutput, Embeddable, Container, - valueClickTrigger, EmbeddableVisTriggerContext, } from '../../../../../../../plugins/embeddable/public'; -import { selectRangeTrigger } from '../../../../../../../plugins/ui_actions/public'; +import { + selectRangeTrigger, + valueClickTrigger, +} from '../../../../../../../plugins/ui_actions/public'; import { dispatchRenderComplete } from '../../../../../../../plugins/kibana_utils/public'; import { IExpressionLoaderParams, diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index aeca97b6040f06..a01c1337122068 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -49,8 +49,11 @@ import { } from './services'; import { createSearchBar } from './ui/search_bar/create_search_bar'; import { esaggs } from './search/expressions'; -import { APPLY_FILTER_TRIGGER, VALUE_CLICK_TRIGGER } from '../../embeddable/public'; -import { SELECT_RANGE_TRIGGER } from '../../ui_actions/public'; +import { + SELECT_RANGE_TRIGGER, + VALUE_CLICK_TRIGGER, + APPLY_FILTER_TRIGGER, +} from '../../ui_actions/public'; import { ACTION_GLOBAL_APPLY_FILTER, createFilterAction, createFiltersFromEvent } from './actions'; import { ApplyGlobalFilterActionContext } from './actions/apply_filter_action'; import { diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts index 78f34e21b9e416..58e8fbae9f9e24 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -19,10 +19,13 @@ import { CoreSetup, Plugin } from 'kibana/server'; import { registerRoutes } from './routes'; +import { indexPatternSavedObjectType } from '../saved_objects'; export class IndexPatternsService implements Plugin { - public setup({ http }: CoreSetup) { - registerRoutes(http); + public setup(core: CoreSetup) { + core.savedObjects.registerType(indexPatternSavedObjectType); + + registerRoutes(core.http); } public start() {} diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index 616e65ad872ab7..efb8759e7beade 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -21,6 +21,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../.. import { IndexPatternsService } from './index_patterns'; import { ISearchSetup } from './search'; import { SearchService } from './search/search_service'; +import { QueryService } from './query/query_service'; import { ScriptsService } from './scripts'; import { KqlTelemetryService } from './kql_telemetry'; import { UsageCollectionSetup } from '../../usage_collection/server'; @@ -47,6 +48,7 @@ export class DataServerPlugin implements Plugin { + public setup(core: CoreSetup) { + core.savedObjects.registerType(querySavedObjectType); + } + + public start() {} +} diff --git a/src/plugins/data/server/saved_objects/index.ts b/src/plugins/data/server/saved_objects/index.ts new file mode 100644 index 00000000000000..5d980974474de8 --- /dev/null +++ b/src/plugins/data/server/saved_objects/index.ts @@ -0,0 +1,22 @@ +/* + * 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. + */ + +export { searchSavedObjectType } from './search'; +export { querySavedObjectType } from './query'; +export { indexPatternSavedObjectType } from './index_patterns'; diff --git a/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts b/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts new file mode 100644 index 00000000000000..b1410e24986676 --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts @@ -0,0 +1,97 @@ +/* + * 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 { SavedObjectMigrationContext } from 'kibana/server'; +import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migrations'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration index-pattern', () => { + describe('6.5.0', () => { + const migrationFn = indexPatternSavedObjectTypeMigrations['6.5.0']; + + test('adds "type" and "typeMeta" properties to object when not declared', () => { + expect( + migrationFn( + { + type: 'index-pattern', + attributes: {}, + }, + savedObjectMigrationContext + ) + ).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "type": undefined, + "typeMeta": undefined, + }, + "type": "index-pattern", +} +`); + }); + + test('keeps "type" and "typeMeta" properties as is when declared', () => { + expect( + migrationFn( + { + type: 'index-pattern', + attributes: { + type: '123', + typeMeta: '123', + }, + }, + savedObjectMigrationContext + ) + ).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "type": "123", + "typeMeta": "123", + }, + "type": "index-pattern", +} +`); + }); + }); + + describe('7.6.0', () => { + const migrationFn = indexPatternSavedObjectTypeMigrations['7.6.0']; + + test('should remove the parent property and update the subType prop on every field that has them', () => { + const input = { + type: 'index-pattern', + attributes: { + title: 'test', + fields: + '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":"multi","parent":"customer_name"}]', + }, + }; + const expected = { + type: 'index-pattern', + attributes: { + title: 'test', + fields: + '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"customer_name"}}}]', + }, + }; + + expect(migrationFn(input, savedObjectMigrationContext)).toEqual(expected); + }); + }); +}); diff --git a/src/plugins/data/server/saved_objects/index_pattern_migrations.ts b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts new file mode 100644 index 00000000000000..7a16386ea484c8 --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts @@ -0,0 +1,60 @@ +/* + * 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 { flow, omit } from 'lodash'; +import { SavedObjectMigrationFn } from 'kibana/server'; + +const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => ({ + ...doc, + attributes: { + ...doc.attributes, + type: doc.attributes.type || undefined, + typeMeta: doc.attributes.typeMeta || undefined, + }, +}); + +const migrateSubTypeAndParentFieldProperties: SavedObjectMigrationFn = doc => { + if (!doc.attributes.fields) return doc; + + const fieldsString = doc.attributes.fields; + const fields = JSON.parse(fieldsString) as any[]; + const migratedFields = fields.map(field => { + if (field.subType === 'multi') { + return { + ...omit(field, 'parent'), + subType: { multi: { parent: field.parent } }, + }; + } + + return field; + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + fields: JSON.stringify(migratedFields), + }, + }; +}; + +export const indexPatternSavedObjectTypeMigrations = { + '6.5.0': flow(migrateAttributeTypeAndAttributeTypeMeta), + '7.6.0': flow(migrateSubTypeAndParentFieldProperties), +}; diff --git a/src/plugins/data/server/saved_objects/index_patterns.ts b/src/plugins/data/server/saved_objects/index_patterns.ts new file mode 100644 index 00000000000000..9838071eee5a4e --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_patterns.ts @@ -0,0 +1,58 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; +import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migrations'; + +export const indexPatternSavedObjectType: SavedObjectsType = { + name: 'index-pattern', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'indexPatternApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'management.kibana.index_patterns', + }; + }, + }, + mappings: { + properties: { + fieldFormatMap: { type: 'text' }, + fields: { type: 'text' }, + intervalName: { type: 'keyword' }, + notExpandable: { type: 'boolean' }, + sourceFilters: { type: 'text' }, + timeFieldName: { type: 'keyword' }, + title: { type: 'text' }, + type: { type: 'keyword' }, + typeMeta: { type: 'keyword' }, + }, + }, + migrations: indexPatternSavedObjectTypeMigrations, +}; diff --git a/src/plugins/data/server/saved_objects/query.ts b/src/plugins/data/server/saved_objects/query.ts new file mode 100644 index 00000000000000..ff0a6cfde8113d --- /dev/null +++ b/src/plugins/data/server/saved_objects/query.ts @@ -0,0 +1,52 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; + +export const querySavedObjectType: SavedObjectsType = { + name: 'query', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'search', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/discover?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`, + uiCapabilitiesPath: 'discover.show', + }; + }, + }, + mappings: { + properties: { + title: { type: 'text' }, + description: { type: 'text' }, + query: { + properties: { language: { type: 'keyword' }, query: { type: 'keyword', index: false } }, + }, + filters: { type: 'object', enabled: false }, + timefilter: { type: 'object', enabled: false }, + }, + }, + migrations: {}, +}; diff --git a/src/plugins/data/server/saved_objects/search.ts b/src/plugins/data/server/saved_objects/search.ts new file mode 100644 index 00000000000000..8b30ff7d08201d --- /dev/null +++ b/src/plugins/data/server/saved_objects/search.ts @@ -0,0 +1,60 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; +import { searchSavedObjectTypeMigrations } from './search_migrations'; + +export const searchSavedObjectType: SavedObjectsType = { + name: 'search', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'discoverApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/objects/savedSearches/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/discover/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'discover.show', + }; + }, + }, + mappings: { + properties: { + columns: { type: 'keyword' }, + description: { type: 'text' }, + hits: { type: 'integer' }, + kibanaSavedObjectMeta: { + properties: { + searchSourceJSON: { type: 'text' }, + }, + }, + sort: { type: 'keyword' }, + title: { type: 'text' }, + version: { type: 'integer' }, + }, + }, + migrations: searchSavedObjectTypeMigrations, +}; diff --git a/src/plugins/data/server/saved_objects/search_migrations.test.ts b/src/plugins/data/server/saved_objects/search_migrations.test.ts new file mode 100644 index 00000000000000..7fdf2e14aefedf --- /dev/null +++ b/src/plugins/data/server/saved_objects/search_migrations.test.ts @@ -0,0 +1,299 @@ +/* + * 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 { SavedObjectMigrationContext } from 'kibana/server'; +import { searchSavedObjectTypeMigrations } from './search_migrations'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration search', () => { + describe('7.0.0', () => { + const migrationFn = searchSavedObjectTypeMigrations['7.0.0']; + + test('skips errors when searchSourceJSON is null', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: null, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": null, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips errors when searchSourceJSON is undefined', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: undefined, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": undefined, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when searchSourceJSON is not a string', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: 123, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": 123, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when searchSourceJSON is invalid json', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: '{abc123}', + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{abc123}", + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when "index" and "filter" is missing from searchSourceJSON', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true}", + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('extracts "index" attribute from doc', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", + }, + }, + "id": "123", + "references": Array [ + Object { + "id": "pattern*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + }, + ], + "type": "search", +} +`); + }); + + test('extracts index patterns from filter', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + bar: true, + filter: [ + { + meta: { + foo: true, + index: 'my-index', + }, + }, + ], + }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", + }, + }, + "id": "123", + "references": Array [ + Object { + "id": "my-index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern", + }, + ], + "type": "search", +} +`); + }); + }); + + describe('7.4.0', function() { + const migrationFn = searchSavedObjectTypeMigrations['7.4.0']; + + test('transforms one dimensional sort arrays into two dimensional arrays', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + sort: ['bytes', 'desc'], + }, + }; + + const expected = { + id: '123', + type: 'search', + attributes: { + sort: [['bytes', 'desc']], + }, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(expected); + }); + + test("doesn't modify search docs that already have two dimensional sort arrays", () => { + const doc = { + id: '123', + type: 'search', + attributes: { + sort: [['bytes', 'desc']], + }, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(doc); + }); + + test("doesn't modify search docs that have no sort array", () => { + const doc = { + id: '123', + type: 'search', + attributes: {}, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(doc); + }); + }); +}); diff --git a/src/plugins/data/server/saved_objects/search_migrations.ts b/src/plugins/data/server/saved_objects/search_migrations.ts new file mode 100644 index 00000000000000..db545e52ce170e --- /dev/null +++ b/src/plugins/data/server/saved_objects/search_migrations.ts @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { flow, get } from 'lodash'; +import { SavedObjectMigrationFn } from 'kibana/server'; + +const migrateIndexPattern: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + if (typeof searchSourceJSON !== 'string') { + return doc; + } + let searchSource; + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + return doc; + } + + if (searchSource.index && Array.isArray(doc.references)) { + searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + doc.references.push({ + name: searchSource.indexRefName, + type: 'index-pattern', + id: searchSource.index, + }); + delete searchSource.index; + } + if (searchSource.filter) { + searchSource.filter.forEach((filterRow: any, i: number) => { + if (!filterRow.meta || !filterRow.meta.index || !Array.isArray(doc.references)) { + return; + } + filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; + doc.references.push({ + name: filterRow.meta.indexRefName, + type: 'index-pattern', + id: filterRow.meta.index, + }); + delete filterRow.meta.index; + }); + } + + doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); + + return doc; +}; + +const setNewReferences: SavedObjectMigrationFn = (doc, context) => { + doc.references = doc.references || []; + // Migrate index pattern + return migrateIndexPattern(doc, context); +}; + +const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { + const sort = get(doc, 'attributes.sort'); + if (!sort) return doc; + + // Don't do anything if we already have a two dimensional array + if (Array.isArray(sort) && sort.length > 0 && Array.isArray(sort[0])) { + return doc; + } + + return { + ...doc, + attributes: { + ...doc.attributes, + sort: [doc.attributes.sort], + }, + }; +}; + +export const searchSavedObjectTypeMigrations = { + '7.0.0': flow(setNewReferences), + '7.4.0': flow(migrateSearchSortToNestedArray), +}; diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 46f90e3c6fc627..5ee19cd3df19f3 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -34,6 +34,8 @@ import { import { IRouteHandlerSearchContext } from './i_route_handler_search_context'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search'; +import { searchSavedObjectType } from '../saved_objects'; + declare module 'kibana/server' { interface RequestHandlerContext { search?: IRouteHandlerSearchContext; @@ -53,6 +55,8 @@ export class SearchService implements Plugin { this.contextContainer = core.context.createContextContainer(); + core.savedObjects.registerType(searchSavedObjectType); + core.http.registerRouteHandlerContext<'search'>('search', context => { return createApi({ caller: context.core.elasticsearch.dataClient.callAsCurrentUser, diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 666df2900c2c39..2a2d9bb414c14f 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -733,7 +733,7 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/server/index.ts:184:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/plugin.ts:62:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/plugin.ts:64:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/embeddable/public/bootstrap.ts b/src/plugins/embeddable/public/bootstrap.ts index 25f1a6ab856614..c8c4f0b95c4587 100644 --- a/src/plugins/embeddable/public/bootstrap.ts +++ b/src/plugins/embeddable/public/bootstrap.ts @@ -17,18 +17,11 @@ * under the License. */ import { UiActionsSetup } from '../../ui_actions/public'; -import { Filter } from '../../data/public'; import { - applyFilterTrigger, contextMenuTrigger, createFilterAction, panelBadgeTrigger, - valueClickTrigger, - EmbeddableVisTriggerContext, - IEmbeddable, EmbeddableContext, - APPLY_FILTER_TRIGGER, - VALUE_CLICK_TRIGGER, CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER, ACTION_ADD_PANEL, @@ -42,11 +35,6 @@ import { declare module '../../ui_actions/public' { export interface TriggerContextMapping { - [VALUE_CLICK_TRIGGER]: EmbeddableVisTriggerContext; - [APPLY_FILTER_TRIGGER]: { - embeddable: IEmbeddable; - filters: Filter[]; - }; [CONTEXT_MENU_TRIGGER]: EmbeddableContext; [PANEL_BADGE_TRIGGER]: EmbeddableContext; } @@ -67,9 +55,7 @@ declare module '../../ui_actions/public' { */ export const bootstrap = (uiActions: UiActionsSetup) => { uiActions.registerTrigger(contextMenuTrigger); - uiActions.registerTrigger(applyFilterTrigger); uiActions.registerTrigger(panelBadgeTrigger); - uiActions.registerTrigger(valueClickTrigger); const actionApplyFilter = createFilterAction(); diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index 178b248f3e29df..1474f9ed630525 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -27,8 +27,6 @@ export { ACTION_ADD_PANEL, AddPanelAction, ACTION_APPLY_FILTER, - APPLY_FILTER_TRIGGER, - applyFilterTrigger, Container, ContainerInput, ContainerOutput, @@ -62,8 +60,6 @@ export { PanelNotFoundError, PanelState, PropertySpec, - VALUE_CLICK_TRIGGER, - valueClickTrigger, ViewMode, withEmbeddableSubscription, } from './lib'; diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts index 22aad4f43c4de0..0052403816eb84 100644 --- a/src/plugins/embeddable/public/lib/triggers/triggers.ts +++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts @@ -33,13 +33,6 @@ export interface EmbeddableVisTriggerContext { }; } -export const VALUE_CLICK_TRIGGER = 'VALUE_CLICK_TRIGGER'; -export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'> = { - id: VALUE_CLICK_TRIGGER, - title: 'Value clicked', - description: 'Value was clicked', -}; - export const CONTEXT_MENU_TRIGGER = 'CONTEXT_MENU_TRIGGER'; export const contextMenuTrigger: Trigger<'CONTEXT_MENU_TRIGGER'> = { id: CONTEXT_MENU_TRIGGER, @@ -47,13 +40,6 @@ export const contextMenuTrigger: Trigger<'CONTEXT_MENU_TRIGGER'> = { description: 'Triggered on top-right corner context-menu select.', }; -export const APPLY_FILTER_TRIGGER = 'FILTER_TRIGGER'; -export const applyFilterTrigger: Trigger<'FILTER_TRIGGER'> = { - id: APPLY_FILTER_TRIGGER, - title: 'Filter click', - description: 'Triggered when user applies filter to an embeddable.', -}; - export const PANEL_BADGE_TRIGGER = 'PANEL_BADGE_TRIGGER'; export const panelBadgeTrigger: Trigger<'PANEL_BADGE_TRIGGER'> = { id: PANEL_BADGE_TRIGGER, diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 721340a270e3db..49b6bd5e176995 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -28,6 +28,15 @@ export { UiActionsSetup, UiActionsStart } from './plugin'; export { UiActionsServiceParams, UiActionsService } from './service'; export { Action, createAction, IncompatibleActionError } from './actions'; export { buildContextMenuForActions } from './context_menu'; -export { Trigger, TriggerContext, SELECT_RANGE_TRIGGER, selectRangeTrigger } from './triggers'; +export { + Trigger, + TriggerContext, + SELECT_RANGE_TRIGGER, + selectRangeTrigger, + VALUE_CLICK_TRIGGER, + valueClickTrigger, + APPLY_FILTER_TRIGGER, + applyFilterTrigger, +} from './triggers'; export { TriggerContextMapping, TriggerId, ActionContextMapping, ActionType } from './types'; export { ActionByType } from './actions'; diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 26a9247c8f0fe4..928e57937a9b5a 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -19,7 +19,7 @@ import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public'; import { UiActionsService } from './service'; -import { selectRangeTrigger } from './triggers'; +import { selectRangeTrigger, valueClickTrigger, applyFilterTrigger } from './triggers'; export type UiActionsSetup = Pick< UiActionsService, @@ -35,6 +35,8 @@ export class UiActionsPlugin implements Plugin { public setup(core: CoreSetup): UiActionsSetup { this.service.registerTrigger(selectRangeTrigger); + this.service.registerTrigger(valueClickTrigger); + this.service.registerTrigger(applyFilterTrigger); return this.service; } diff --git a/src/legacy/core_plugins/data/mappings.ts b/src/plugins/ui_actions/public/triggers/apply_filter_trigger.ts similarity index 60% rename from src/legacy/core_plugins/data/mappings.ts rename to src/plugins/ui_actions/public/triggers/apply_filter_trigger.ts index 90777ec8e36512..7a95709ac28ba5 100644 --- a/src/legacy/core_plugins/data/mappings.ts +++ b/src/plugins/ui_actions/public/triggers/apply_filter_trigger.ts @@ -17,34 +17,11 @@ * under the License. */ -export const mappings = { - query: { - properties: { - title: { - type: 'text', - }, - description: { - type: 'text', - }, - query: { - properties: { - language: { - type: 'keyword', - }, - query: { - type: 'keyword', - index: false, - }, - }, - }, - filters: { - type: 'object', - enabled: false, - }, - timefilter: { - type: 'object', - enabled: false, - }, - }, - }, +import { Trigger } from '.'; + +export const APPLY_FILTER_TRIGGER = 'FILTER_TRIGGER'; +export const applyFilterTrigger: Trigger<'FILTER_TRIGGER'> = { + id: APPLY_FILTER_TRIGGER, + title: 'Filter click', + description: 'Triggered when user applies filter to an embeddable.', }; diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts index 2ea21ab46e880f..a5bf9e1822941d 100644 --- a/src/plugins/ui_actions/public/triggers/index.ts +++ b/src/plugins/ui_actions/public/triggers/index.ts @@ -21,3 +21,5 @@ export * from './trigger'; export * from './trigger_contract'; export * from './trigger_internal'; export * from './select_range_trigger'; +export * from './value_click_trigger'; +export * from './apply_filter_trigger'; diff --git a/src/plugins/ui_actions/public/triggers/value_click_trigger.ts b/src/plugins/ui_actions/public/triggers/value_click_trigger.ts new file mode 100644 index 00000000000000..ad32bdc1b564e1 --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/value_click_trigger.ts @@ -0,0 +1,27 @@ +/* + * 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 { Trigger } from '.'; + +export const VALUE_CLICK_TRIGGER = 'VALUE_CLICK_TRIGGER'; +export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'> = { + id: VALUE_CLICK_TRIGGER, + title: 'Value clicked', + description: 'Value was clicked', +}; diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index fb55b192a2fa65..c7e6d61e15f317 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -19,8 +19,9 @@ import { ActionByType } from './actions/action'; import { TriggerInternal } from './triggers/trigger_internal'; -import { EmbeddableVisTriggerContext } from '../../embeddable/public'; -import { SELECT_RANGE_TRIGGER } from './triggers'; +import { EmbeddableVisTriggerContext, IEmbeddable } from '../../embeddable/public'; +import { Filter } from '../../data/public'; +import { SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER, APPLY_FILTER_TRIGGER } from './triggers'; export type TriggerRegistry = Map>; export type ActionRegistry = Map>; @@ -36,6 +37,11 @@ export type TriggerContext = BaseContext; export interface TriggerContextMapping { [DEFAULT_TRIGGER]: TriggerContext; [SELECT_RANGE_TRIGGER]: EmbeddableVisTriggerContext; + [VALUE_CLICK_TRIGGER]: EmbeddableVisTriggerContext; + [APPLY_FILTER_TRIGGER]: { + embeddable: IEmbeddable; + filters: Filter[]; + }; } const DEFAULT_ACTION = ''; diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json index cf79ce17293d61..8e63ea78333270 100644 --- a/src/plugins/visualizations/kibana.json +++ b/src/plugins/visualizations/kibana.json @@ -1,7 +1,7 @@ { "id": "visualizations", "version": "kibana", - "server": false, + "server": true, "ui": true, "requiredPlugins": [ "expressions" diff --git a/src/plugins/visualizations/server/index.ts b/src/plugins/visualizations/server/index.ts new file mode 100644 index 00000000000000..80c10c3945d4c7 --- /dev/null +++ b/src/plugins/visualizations/server/index.ts @@ -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 { PluginInitializerContext } from '../../../core/server'; +import { VisualizationsPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new VisualizationsPlugin(initializerContext); +} + +export { VisualizationsPluginSetup, VisualizationsPluginStart } from './types'; diff --git a/src/plugins/visualizations/server/plugin.ts b/src/plugins/visualizations/server/plugin.ts new file mode 100644 index 00000000000000..79cce6b5867a1a --- /dev/null +++ b/src/plugins/visualizations/server/plugin.ts @@ -0,0 +1,54 @@ +/* + * 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 { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + Logger, +} from '../../../core/server'; + +import { visualizationSavedObjectType } from './saved_objects'; + +import { VisualizationsPluginSetup, VisualizationsPluginStart } from './types'; + +export class VisualizationsPlugin + implements Plugin { + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + this.logger.debug('visualizations: Setup'); + + core.savedObjects.registerType(visualizationSavedObjectType); + + return {}; + } + + public start(core: CoreStart) { + this.logger.debug('visualizations: Started'); + return {}; + } + + public stop() {} +} diff --git a/src/plugins/visualizations/server/saved_objects/index.ts b/src/plugins/visualizations/server/saved_objects/index.ts new file mode 100644 index 00000000000000..be75f635824500 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/index.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export { visualizationSavedObjectType } from './visualization'; diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts new file mode 100644 index 00000000000000..9f4782f3ec7308 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization.ts @@ -0,0 +1,56 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; +import { visualizationSavedObjectTypeMigrations } from './visualization_migrations'; + +export const visualizationSavedObjectType: SavedObjectsType = { + name: 'visualization', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'visualizeApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/objects/savedVisualizations/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/visualize/edit/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'visualize.show', + }; + }, + }, + mappings: { + properties: { + description: { type: 'text' }, + kibanaSavedObjectMeta: { properties: { searchSourceJSON: { type: 'text' } } }, + savedSearchRefName: { type: 'keyword' }, + title: { type: 'text' }, + uiStateJSON: { type: 'text' }, + version: { type: 'integer' }, + visState: { type: 'text' }, + }, + }, + migrations: visualizationSavedObjectTypeMigrations, +}; diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts new file mode 100644 index 00000000000000..02c114bad4e725 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts @@ -0,0 +1,1356 @@ +/* + * 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 { visualizationSavedObjectTypeMigrations } from './visualization_migrations'; +import { SavedObjectMigrationContext, SavedObjectMigrationFn } from 'kibana/server'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration visualization', () => { + describe('6.7.2', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['6.7.2']( + doc as Parameters[0], + savedObjectMigrationContext + ); + let doc: any; + + describe('date histogram time zone removal', () => { + beforeEach(() => { + doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + enabled: true, + id: '1', + params: { + // Doesn't make much sense but we want to test it's not removing it from anything else + time_zone: 'Europe/Berlin', + }, + schema: 'metric', + type: 'count', + }, + { + enabled: true, + id: '2', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + time_zone: 'Europe/Berlin', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '4', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '3', + params: { + customBucket: { + enabled: true, + id: '1-bucket', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + time_zone: 'Europe/Berlin', + useNormalizedEsInterval: true, + }, + type: 'date_histogram', + }, + customMetric: { + enabled: true, + id: '1-metric', + params: {}, + type: 'count', + }, + }, + schema: 'metric', + type: 'max_bucket', + }, + ], + }), + }, + } as Parameters[0]; + }); + + it('should remove time_zone from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[1]).not.toHaveProperty('params.time_zone'); + }); + + it('should not remove time_zone from non date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[0]).toHaveProperty('params.time_zone'); + }); + + it('should remove time_zone from nested aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); + }); + + it('should not fail on date histograms without a time_zone', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[2]).not.toHaveProperty('params.time_zone'); + }); + + it('should be able to apply the migration twice, since we need it for 6.7.2 and 7.0.1', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[1]).not.toHaveProperty('params.time_zone'); + expect(aggs[0]).toHaveProperty('params.time_zone'); + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); + expect(aggs[2]).not.toHaveProperty('params.time_zone'); + }); + }); + }); + + describe('7.0.0', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.0.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const generateDoc = (type: any, aggs: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ type, aggs }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + references: [], + }); + + it('does not throw error on empty object', () => { + const migratedDoc = migrate({ + attributes: { + visState: '{}', + }, + }); + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{}", + }, + "references": Array [], +} +`); + }); + + it('skips errors when searchSourceJSON is null', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: null, + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": null, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips errors when searchSourceJSON is undefined', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: undefined, + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": undefined, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when searchSourceJSON is not a string', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: 123, + }, + savedSearchId: '123', + }, + }; + + expect(migrate(doc)).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": 123, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when searchSourceJSON is invalid json', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{abc123}', + }, + savedSearchId: '123', + }, + }; + + expect(migrate(doc)).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{abc123}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when "index" and "filter" is missing from searchSourceJSON', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts "index" attribute from doc', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "pattern*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + }, + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts index patterns from the filter', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + bar: true, + filter: [ + { + meta: { index: 'my-index', foo: true }, + }, + ], + }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "my-index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern", + }, + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts index patterns from controls', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + foo: true, + visState: JSON.stringify({ + bar: false, + params: { + controls: [ + { + bar: true, + indexPattern: 'pattern*', + }, + { + foo: true, + }, + ], + }, + }), + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "visState": "{\\"bar\\":false,\\"params\\":{\\"controls\\":[{\\"bar\\":true,\\"indexPatternRefName\\":\\"control_0_index_pattern\\"},{\\"foo\\":true}]}}", + }, + "id": "1", + "references": Array [ + Object { + "id": "pattern*", + "name": "control_0_index_pattern", + "type": "index-pattern", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips extracting savedSearchId when missing', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "visState": "{}", + }, + "id": "1", + "references": Array [], +} +`); + }); + + it('extract savedSearchId from doc', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], +} +`); + }); + + it('delete savedSearchId when empty string in doc', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + savedSearchId: '', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "visState": "{}", + }, + "id": "1", + "references": Array [], +} +`); + }); + + it('should return a new object if vis is table and has multiple split aggs', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + ]; + const tableDoc = generateDoc('table', aggs); + const expected = tableDoc; + const actual = migrate(tableDoc); + + expect(actual).not.toEqual(expected); + }); + + it('should not touch any vis that is not table', () => { + const pieDoc = generateDoc('pie', []); + const expected = pieDoc; + const actual = migrate(pieDoc); + + expect(actual).toEqual(expected); + }); + + it('should not change values in any vis that is not table', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'segment', + params: { hey: 'ya' }, + }, + ]; + const pieDoc = generateDoc('pie', aggs); + const expected = pieDoc; + const actual = migrate(pieDoc); + + expect(actual).toEqual(expected); + }); + + it('should not touch table vis if there are not multiple split aggs', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + ]; + const tableDoc = generateDoc('table', aggs); + const expected = tableDoc; + const actual = migrate(tableDoc); + + expect(actual).toEqual(expected); + }); + + it('should change all split aggs to `bucket` except the first', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + { + id: '4', + schema: 'bucket', + params: { heyyy: 'yaaa' }, + }, + ]; + const expected = ['metric', 'split', 'bucket', 'bucket']; + const migrated = migrate(generateDoc('table', aggs)); + const actual = JSON.parse(migrated.attributes.visState); + + expect(actual.aggs.map((agg: any) => agg.schema)).toEqual(expected); + }); + + it('should remove `rows` param from any aggs that are not `split`', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + ]; + const expected = [{}, { foo: 'bar', row: true }, { hey: 'ya' }]; + const migrated = migrate(generateDoc('table', aggs)); + const actual = JSON.parse(migrated.attributes.visState); + + expect(actual.aggs.map((agg: any) => agg.params)).toEqual(expected); + }); + + it('should throw with a reference to the doc name if something goes wrong', () => { + const doc = { + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: '!/// Intentionally malformed JSON ///!', + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }; + expect(() => migrate(doc)).toThrowError(/My Vis/); + }); + }); + + describe('7.2.0', () => { + describe('date histogram custom interval removal', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.2.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + let doc: any; + + beforeEach(() => { + doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + enabled: true, + id: '1', + params: { + customInterval: '1h', + }, + schema: 'metric', + type: 'count', + }, + { + enabled: true, + id: '2', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '4', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'custom', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '3', + params: { + customBucket: { + enabled: true, + id: '1-bucket', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'custom', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + type: 'date_histogram', + }, + customMetric: { + enabled: true, + id: '1-metric', + params: {}, + type: 'count', + }, + }, + schema: 'metric', + type: 'max_bucket', + }, + ], + }), + }, + }; + }); + + it('should remove customInterval from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[1]).not.toHaveProperty('params.customInterval'); + }); + + it('should not change interval from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[1].params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[1].params.interval + ); + }); + + it('should not remove customInterval from non date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[0]).toHaveProperty('params.customInterval'); + }); + + it('should set interval with customInterval value and remove customInterval when interval equals "custom"', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[2].params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[2].params.customInterval + ); + expect(aggs[2]).not.toHaveProperty('params.customInterval'); + }); + + it('should remove customInterval from nested aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); + }); + + it('should remove customInterval from nested aggregations and set interval with customInterval value', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3].params.customBucket.params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[3].params.customBucket.params.customInterval + ); + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); + }); + + it('should not fail on date histograms without a customInterval', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3]).not.toHaveProperty('params.customInterval'); + }); + }); + }); + + describe('7.3.0', () => { + const logMsgArr: string[] = []; + const logger = ({ + log: { + warn: (msg: string) => logMsgArr.push(msg), + }, + } as unknown) as SavedObjectMigrationContext; + + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.0']( + doc as Parameters[0], + logger + ); + + it('migrates type = gauge verticalSplit: false to alignment: vertical', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: false } } }), + }, + }); + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"horizontal\\"}}}", + }, +} +`); + }); + + it('migrates type = gauge verticalSplit: false to alignment: horizontal', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: true } } }), + }, + }); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"vertical\\"}}}", + }, +} +`); + }); + + it('doesnt migrate type = gauge containing invalid visState object, adds message to log', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge' }), + }, + }); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\"}", + }, +} +`); + expect(logMsgArr).toMatchInlineSnapshot(` +Array [ + "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", + "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", +] +`); + }); + + describe('filters agg query migration', () => { + const doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + type: 'filters', + params: { + filters: [ + { + input: { + query: 'response:200', + }, + label: '', + }, + { + input: { + query: 'response:404', + }, + label: 'bad response', + }, + { + input: { + query: { + exists: { + field: 'phpmemory', + }, + }, + }, + label: '', + }, + ], + }, + }, + ], + }), + }, + }; + + it('should add language property to filters without one, assuming lucene', () => { + const migrationResult = migrate(doc); + + expect(migrationResult).toEqual({ + attributes: { + visState: JSON.stringify({ + aggs: [ + { + type: 'filters', + params: { + filters: [ + { + input: { + query: 'response:200', + language: 'lucene', + }, + label: '', + }, + { + input: { + query: 'response:404', + language: 'lucene', + }, + label: 'bad response', + }, + { + input: { + query: { + exists: { + field: 'phpmemory', + }, + }, + language: 'lucene', + }, + label: '', + }, + ], + }, + }, + ], + }), + }, + }); + }); + }); + + describe('replaceMovAvgToMovFn()', () => { + let doc: any; + + beforeEach(() => { + doc = { + attributes: { + title: 'VIS', + visState: `{"title":"VIS","type":"metrics","params":{"id":"61ca57f0-469d-11e7-af02-69e470af7417", + "type":"timeseries","series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","color":"rgba(0,156,224,1)", + "split_mode":"terms","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417","type":"count", + "numerator":"FlightDelay:true"},{"settings":"","minimize":0,"window":5,"model": + "holt_winters","id":"23054fe0-8915-11e9-9b86-d3f94982620f","type":"moving_average","field": + "61ca57f2-469d-11e7-af02-69e470af7417","predict":1}],"separate_axis":0,"axis_position":"right", + "formatter":"number","chart_type":"line","line_width":"2","point_size":"0","fill":0.5,"stacked":"none", + "label":"Percent Delays","terms_size":"2","terms_field":"OriginCityName"}],"time_field":"timestamp", + "index_pattern":"kibana_sample_data_flights","interval":">=12h","axis_position":"left","axis_formatter": + "number","show_legend":1,"show_grid":1,"annotations":[{"fields":"FlightDelay,Cancelled,Carrier", + "template":"{{Carrier}}: Flight Delayed and Cancelled!","index_pattern":"kibana_sample_data_flights", + "query_string":"FlightDelay:true AND Cancelled:true","id":"53b7dff0-4c89-11e8-a66a-6989ad5a0a39", + "color":"rgba(0,98,177,1)","time_field":"timestamp","icon":"fa-exclamation-triangle", + "ignore_global_filters":1,"ignore_panel_filters":1,"hidden":true}],"legend_position":"bottom", + "axis_scale":"normal","default_index_pattern":"kibana_sample_data_flights","default_timefield":"timestamp"}, + "aggs":[]}`, + }, + migrationVersion: { + visualization: '7.2.0', + }, + type: 'visualization', + }; + }); + + test('should add some necessary moving_fn fields', () => { + const migratedDoc = migrate(doc); + const visState = JSON.parse(migratedDoc.attributes.visState); + const metric = visState.params.series[0].metrics[1]; + + expect(metric).toHaveProperty('model_type'); + expect(metric).toHaveProperty('alpha'); + expect(metric).toHaveProperty('beta'); + expect(metric).toHaveProperty('gamma'); + expect(metric).toHaveProperty('period'); + expect(metric).toHaveProperty('multiplicative'); + }); + }); + }); + + describe('7.3.0 tsvb', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const generateDoc = (params: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ params }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }); + it('should change series item filters from a string into an object', () => { + const params = { type: 'metric', series: [{ filter: 'Filter Bytes Test:>1000' }] }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[0].filter).toHaveProperty('query'); + expect(series[0].filter).toHaveProperty('language'); + }); + it('should not change a series item filter string in the object after migration', () => { + const markdownParams = { + type: 'markdown', + series: [ + { + filter: 'Filter Bytes Test:>1000', + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + }; + const markdownDoc = generateDoc(markdownParams); + const migratedMarkdownDoc = migrate(markdownDoc); + const markdownSeries = JSON.parse(migratedMarkdownDoc.attributes.visState).params.series; + + expect(markdownSeries[0].filter.query).toBe( + JSON.parse(markdownDoc.attributes.visState).params.series[0].filter + ); + expect(markdownSeries[0].split_filters[0].filter.query).toBe( + JSON.parse(markdownDoc.attributes.visState).params.series[0].split_filters[0].filter + ); + }); + + it('should change series item filters from a string into an object for all filters', () => { + const params = { + type: 'timeseries', + filter: 'bytes:>1000', + series: [ + { + filter: 'Filter Bytes Test:>1000', + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + annotations: [{ query_string: 'bytes:>1000' }], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(Object.keys(timeSeriesParams.series[0].filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(Object.keys(timeSeriesParams.series[0].split_filters[0].filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(Object.keys(timeSeriesParams.annotations[0].query_string)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + }); + + it('should not fail on a metric visualization without a filter in a series item', () => { + const params = { type: 'metric', series: [{}, {}, {}] }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[2]).not.toHaveProperty('filter.query'); + }); + + it('should not migrate a visualization of unknown type', () => { + const params = { type: 'unknown', series: [{ filter: 'foo:bar' }] }; + const doc = generateDoc(params); + const migratedDoc = migrate(doc); + const series = JSON.parse(migratedDoc.attributes.visState).params.series; + + expect(series[0].filter).toEqual(params.series[0].filter); + }); + }); + + describe('7.3.1', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.1']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + it('should migrate filters agg query string queries', () => { + const state = { + aggs: [ + { type: 'count', params: {} }, + { + type: 'filters', + params: { + filters: [ + { + input: { + query: { + query_string: { query: 'machine.os.keyword:"win 8"' }, + }, + }, + }, + ], + }, + }, + ], + }; + const expected = { + aggs: [ + { type: 'count', params: {} }, + { + type: 'filters', + params: { + filters: [{ input: { query: 'machine.os.keyword:"win 8"' } }], + }, + }, + ], + }; + const migratedDoc = migrate({ attributes: { visState: JSON.stringify(state) } }); + + expect(migratedDoc).toEqual({ attributes: { visState: JSON.stringify(expected) } }); + }); + }); + + describe('7.4.2 tsvb split_filters migration', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.4.2']( + doc as Parameters[0], + savedObjectMigrationContext + ); + const generateDoc = (params: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ params }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }); + + it('should change series item filters from a string into an object for all filters', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(Object.keys(timeSeriesParams.filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ + query: 'bytes:>1000', + language: 'lucene', + }); + }); + + it('should change series item split filters when there is no filter item', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + annotations: [ + { + query_string: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ + query: 'bytes:>1000', + language: 'lucene', + }); + }); + + it('should not convert split_filters to objects if there are no split filter filters', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [], + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters).not.toHaveProperty('query'); + }); + + it('should do nothing if a split_filter is already a query:language object', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [ + { + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }, + ], + annotations: [ + { + query_string: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters[0].filter.query).toEqual('bytes:>1000'); + expect(timeSeriesParams.series[0].split_filters[0].filter.language).toEqual('lucene'); + }); + }); +}); diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts new file mode 100644 index 00000000000000..9ee355cbb23cfe --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts @@ -0,0 +1,574 @@ +/* + * 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 { SavedObjectMigrationFn } from 'kibana/server'; +import { cloneDeep, get, omit, has, flow } from 'lodash'; + +const migrateIndexPattern: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + if (typeof searchSourceJSON !== 'string') { + return doc; + } + let searchSource; + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + return doc; + } + + if (searchSource.index && Array.isArray(doc.references)) { + searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + doc.references.push({ + name: searchSource.indexRefName, + type: 'index-pattern', + id: searchSource.index, + }); + delete searchSource.index; + } + if (searchSource.filter) { + searchSource.filter.forEach((filterRow: any, i: number) => { + if (!filterRow.meta || !filterRow.meta.index || !Array.isArray(doc.references)) { + return; + } + filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; + doc.references.push({ + name: filterRow.meta.indexRefName, + type: 'index-pattern', + id: filterRow.meta.index, + }); + delete filterRow.meta.index; + }); + } + + doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); + + return doc; +}; + +// [TSVB] Migrate percentile-rank aggregation (value -> values) +const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState && visState.type === 'metrics') { + const series: any[] = get(visState, 'params.series') || []; + + series.forEach(part => { + (part.metrics || []).forEach((metric: any) => { + if (metric.type === 'percentile_rank' && has(metric, 'value')) { + metric.values = [metric.value]; + + delete metric.value; + } + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } + return doc; +}; + +// Migrate date histogram aggregation (remove customInterval) +const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type === 'date_histogram' && agg.params) { + if (agg.params.interval === 'custom') { + agg.params.interval = agg.params.customInterval; + } + delete agg.params.customInterval; + } + + if ( + get(agg, 'params.customBucket.type', null) === 'date_histogram' && + agg.params.customBucket.params + ) { + if (agg.params.customBucket.params.interval === 'custom') { + agg.params.customBucket.params.interval = agg.params.customBucket.params.customInterval; + } + delete agg.params.customBucket.params.customInterval; + } + }); + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } + return doc; +}; + +const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + // We're checking always for the existance of agg.params here. This should always exist, but better + // be safe then sorry during migrations. + if (agg.type === 'date_histogram' && agg.params) { + delete agg.params.time_zone; + } + + if ( + get(agg, 'params.customBucket.type', null) === 'date_histogram' && + agg.params.customBucket.params + ) { + delete agg.params.customBucket.params.time_zone; + } + }); + doc.attributes.visState = JSON.stringify(visState); + } + } + return doc; +}; + +// migrate gauge verticalSplit to alignment +// https://github.com/elastic/kibana/issues/34636 +const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + if (visState && visState.type === 'gauge' && !visState.params.gauge.alignment) { + visState.params.gauge.alignment = visState.params.gauge.verticalSplit + ? 'vertical' + : 'horizontal'; + delete visState.params.gauge.verticalSplit; + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + logger.log.warn(`Exception @ migrateGaugeVerticalSplitToAlignment! ${e}`); + logger.log.warn(`Exception @ migrateGaugeVerticalSplitToAlignment! Payload: ${visStateJSON}`); + } + } + return doc; +}; +// Migrate filters (string -> { query: string, language: lucene }) +/* + Enabling KQL in TSVB causes problems with savedObject visualizations when these are saved with filters. + In a visualisation type of saved object, if the visState param is of type metric, the filter is saved as a string that is not interpretted correctly as a lucene query in the visualization itself. + We need to transform the filter string into an object containing the original string as a query and specify the query language as lucene. + For Metrics visualizations (param.type === "metric"), filters can be applied to each series object in the series array within the SavedObject.visState.params object. + Path to the series array is thus: + attributes.visState. +*/ +const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) => { + // Migrate filters + // If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly + const newDoc = cloneDeep(doc); + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const visType = get(visState, 'params.type'); + const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; + if (tsvbTypes.indexOf(visType) === -1) { + // skip + return doc; + } + // migrate the params fitler + const params: any = get(visState, 'params'); + if (params.filter && typeof params.filter === 'string') { + const paramsFilterObject = { + query: params.filter, + language: 'lucene', + }; + params.filter = paramsFilterObject; + } + + // migrate the annotations query string: + const annotations: any[] = get(visState, 'params.annotations') || []; + annotations.forEach(item => { + if (!item.query_string) { + // we don't need to transform anything if there isn't a filter at all + return; + } + if (typeof item.query_string === 'string') { + const itemQueryStringObject = { + query: item.query_string, + language: 'lucene', + }; + item.query_string = itemQueryStringObject; + } + }); + // migrate the series filters + const series: any[] = get(visState, 'params.series') || []; + + series.forEach(item => { + if (!item.filter) { + // we don't need to transform anything if there isn't a filter at all + return; + } + // series item filter + if (typeof item.filter === 'string') { + const itemfilterObject = { + query: item.filter, + language: 'lucene', + }; + item.filter = itemfilterObject; + } + // series item split filters filter + if (item.split_filters) { + const splitFilters: any[] = get(item, 'split_filters') || []; + splitFilters.forEach(filter => { + if (!filter.filter) { + // we don't need to transform anything if there isn't a filter at all + return; + } + if (typeof filter.filter === 'string') { + const filterfilterObject = { + query: filter.filter, + language: 'lucene', + }; + filter.filter = filterfilterObject; + } + }); + } + }); + newDoc.attributes.visState = JSON.stringify(visState); + } + } + return newDoc; +}; + +const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => { + // Migrate split_filters in TSVB objects that weren't migrated in 7.3 + // If any filters exist and they are a string, we assume them to be lucene syntax and transform the filter into an object accordingly + const newDoc = cloneDeep(doc); + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const visType = get(visState, 'params.type'); + const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; + if (tsvbTypes.indexOf(visType) === -1) { + // skip + return doc; + } + // migrate the series split_filter filters + const series: any[] = get(visState, 'params.series') || []; + series.forEach(item => { + // series item split filters filter + if (item.split_filters) { + const splitFilters: any[] = get(item, 'split_filters') || []; + if (splitFilters.length > 0) { + // only transform split_filter filters if we have filters + splitFilters.forEach(filter => { + if (typeof filter.filter === 'string') { + const filterfilterObject = { + query: filter.filter, + language: 'lucene', + }; + filter.filter = filterfilterObject; + } + }); + } + } + }); + newDoc.attributes.visState = JSON.stringify(visState); + } + } + return newDoc; +}; + +const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type !== 'filters') return; + + agg.params.filters.forEach((filter: any) => { + if (filter.input.language) return filter; + filter.input.language = 'lucene'; + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + } + return doc; +}; + +const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + + if (visState && visState.type === 'metrics') { + const series: any[] = get(visState, 'params.series', []); + + series.forEach(part => { + if (part.metrics && Array.isArray(part.metrics)) { + part.metrics.forEach((metric: any) => { + if (metric.type === 'moving_average') { + metric.model_type = metric.model; + metric.alpha = get(metric, 'settings.alpha', 0.3); + metric.beta = get(metric, 'settings.beta', 0.1); + metric.gamma = get(metric, 'settings.gamma', 0.3); + metric.period = get(metric, 'settings.period', 1); + metric.multiplicative = get(metric, 'settings.type') === 'mult'; + + delete metric.minimize; + delete metric.model; + delete metric.settings; + delete metric.predict; + } + }); + } + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + logger.log.warn(`Exception @ replaceMovAvgToMovFn! ${e}`); + logger.log.warn(`Exception @ replaceMovAvgToMovFn! Payload: ${visStateJSON}`); + } + } + + return doc; +}; + +const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type !== 'filters') return doc; + + agg.params.filters.forEach((filter: any) => { + if (filter.input.query.query_string) { + filter.input.query = filter.input.query.query_string.query; + } + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + } + return doc; +}; + +const addDocReferences: SavedObjectMigrationFn = doc => ({ + ...doc, + references: doc.references || [], +}); + +const migrateSavedSearch: SavedObjectMigrationFn = doc => { + const savedSearchId = get(doc, 'attributes.savedSearchId'); + + if (savedSearchId && doc.references) { + doc.references.push({ + type: 'search', + name: 'search_0', + id: savedSearchId, + }); + doc.attributes.savedSearchRefName = 'search_0'; + } + + delete doc.attributes.savedSearchId; + + return doc; +}; + +const migrateControls: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const controls: any[] = get(visState, 'params.controls') || []; + controls.forEach((control, i) => { + if (!control.indexPattern || !doc.references) { + return; + } + control.indexPatternRefName = `control_${i}_index_pattern`; + doc.references.push({ + name: control.indexPatternRefName, + type: 'index-pattern', + id: control.indexPattern, + }); + delete control.indexPattern; + }); + doc.attributes.visState = JSON.stringify(visState); + } + } + + return doc; +}; + +const migrateTableSplits: SavedObjectMigrationFn = doc => { + try { + const visState = JSON.parse(doc.attributes.visState); + if (get(visState, 'type') !== 'table') { + return doc; // do nothing; we only want to touch tables + } + + let splitCount = 0; + visState.aggs = visState.aggs.map((agg: any) => { + if (agg.schema !== 'split') { + return agg; + } + + splitCount++; + if (splitCount === 1) { + return agg; // leave the first split agg unchanged + } + agg.schema = 'bucket'; + // the `row` param is exclusively used by split aggs, so we remove it + agg.params = omit(agg.params, ['row']); + return agg; + }); + + if (splitCount <= 1) { + return doc; // do nothing; we only want to touch tables with multiple split aggs + } + + const newDoc = cloneDeep(doc); + newDoc.attributes.visState = JSON.stringify(visState); + + return newDoc; + } catch (e) { + throw new Error(`Failure attempting to migrate saved object '${doc.attributes.title}' - ${e}`); + } +}; + +export const visualizationSavedObjectTypeMigrations = { + /** + * We need to have this migration twice, once with a version prior to 7.0.0 once with a version + * after it. The reason for that is, that this migration has been introduced once 7.0.0 was already + * released. Thus a user who already had 7.0.0 installed already got the 7.0.0 migrations below running, + * so we need a version higher than that. But this fix was backported to the 6.7 release, meaning if we + * would only have the 7.0.1 migration in here a user on the 6.7 release will migrate their saved objects + * to the 7.0.1 state, and thus when updating their Kibana to 7.0, will never run the 7.0.0 migrations introduced + * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 + * only contained the 6.7.2 migration and not the 7.0.1 migration. + */ + '6.7.2': flow(removeDateHistogramTimeZones), + '7.0.0': flow( + addDocReferences, + migrateIndexPattern, + migrateSavedSearch, + migrateControls, + migrateTableSplits + ), + '7.0.1': flow(removeDateHistogramTimeZones), + '7.2.0': flow( + migratePercentileRankAggregation, + migrateDateHistogramAggregation + ), + '7.3.0': flow( + migrateGaugeVerticalSplitToAlignment, + transformFilterStringToQueryObject, + migrateFiltersAggQuery, + replaceMovAvgToMovFn + ), + '7.3.1': flow(migrateFiltersAggQueryStringQueries), + '7.4.2': flow(transformSplitFiltersStringToQueryObject), +}; diff --git a/src/plugins/visualizations/server/types.ts b/src/plugins/visualizations/server/types.ts new file mode 100644 index 00000000000000..6924edb29627d9 --- /dev/null +++ b/src/plugins/visualizations/server/types.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisualizationsPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisualizationsPluginStart {} diff --git a/x-pack/legacy/plugins/graph/index.ts b/x-pack/legacy/plugins/graph/index.ts index b2d6fd3957d642..5122796335e457 100644 --- a/x-pack/legacy/plugins/graph/index.ts +++ b/x-pack/legacy/plugins/graph/index.ts @@ -4,31 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { resolve } from 'path'; import { i18n } from '@kbn/i18n'; // @ts-ignore import migrations from './migrations'; import mappings from './mappings.json'; import { LegacyPluginInitializer } from '../../../../src/legacy/plugin_discovery/types'; -import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; export const graph: LegacyPluginInitializer = kibana => { return new kibana.Plugin({ id: 'graph', configPrefix: 'xpack.graph', - publicDir: resolve(__dirname, 'public'), require: ['kibana', 'elasticsearch', 'xpack_main'], uiExports: { - app: { - title: 'Graph', - order: 9000, - icon: 'plugins/graph/icon.png', - euiIconType: 'graphApp', - main: 'plugins/graph/index', - category: DEFAULT_APP_CATEGORIES.analyze, - }, - styleSheetPaths: resolve(__dirname, 'public/index.scss'), mappings, migrations, }, diff --git a/x-pack/legacy/plugins/graph/public/icon.png b/x-pack/legacy/plugins/graph/public/icon.png deleted file mode 100644 index f2a16437209c79..00000000000000 Binary files a/x-pack/legacy/plugins/graph/public/icon.png and /dev/null differ diff --git a/x-pack/legacy/plugins/graph/public/index.ts b/x-pack/legacy/plugins/graph/public/index.ts deleted file mode 100644 index fb60a66fb28ccd..00000000000000 --- a/x-pack/legacy/plugins/graph/public/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 { npSetup, npStart } from 'ui/new_platform'; -import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; -import { GraphPlugin } from './plugin'; -import { GraphSetup } from '../../../../plugins/graph/public'; - -type XpackNpSetupDeps = typeof npSetup.plugins & { - licensing: LicensingPluginSetup; - graph: GraphSetup; -}; - -(async () => { - const instance = new GraphPlugin(); - instance.setup(npSetup.core, { - ...(npSetup.plugins as XpackNpSetupDeps), - }); - instance.start(npStart.core, { - npData: npStart.plugins.data, - navigation: npStart.plugins.navigation, - }); -})(); diff --git a/x-pack/legacy/plugins/graph/public/plugin.ts b/x-pack/legacy/plugins/graph/public/plugin.ts deleted file mode 100644 index 4ccaf6b5dfa277..00000000000000 --- a/x-pack/legacy/plugins/graph/public/plugin.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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. - */ - -// NP type imports -import { - AppMountParameters, - CoreSetup, - CoreStart, - Plugin, - SavedObjectsClientContract, -} from 'src/core/public'; -import { Plugin as DataPlugin } from 'src/plugins/data/public'; -import { Storage } from '../../../../../src/plugins/kibana_utils/public'; -import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; -import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public'; -import { initAngularBootstrap } from '../../../../../src/plugins/kibana_legacy/public'; -import { GraphSetup } from '../../../../plugins/graph/public'; - -export interface GraphPluginStartDependencies { - npData: ReturnType; - navigation: NavigationStart; -} - -export interface GraphPluginSetupDependencies { - licensing: LicensingPluginSetup; - graph: GraphSetup; -} - -export class GraphPlugin implements Plugin { - private navigationStart: NavigationStart | null = null; - private npDataStart: ReturnType | null = null; - private savedObjectsClient: SavedObjectsClientContract | null = null; - - setup(core: CoreSetup, { licensing, graph }: GraphPluginSetupDependencies) { - initAngularBootstrap(); - core.application.register({ - id: 'graph', - title: 'Graph', - mount: async (params: AppMountParameters) => { - const [coreStart] = await core.getStartServices(); - const { renderApp } = await import('./application'); - return renderApp({ - ...params, - licensing, - navigation: this.navigationStart!, - npData: this.npDataStart!, - savedObjectsClient: this.savedObjectsClient!, - addBasePath: core.http.basePath.prepend, - getBasePath: core.http.basePath.get, - canEditDrillDownUrls: graph.config.canEditDrillDownUrls, - graphSavePolicy: graph.config.savePolicy, - storage: new Storage(window.localStorage), - capabilities: coreStart.application.capabilities.graph, - coreStart, - chrome: coreStart.chrome, - config: coreStart.uiSettings, - toastNotifications: coreStart.notifications.toasts, - indexPatterns: this.npDataStart!.indexPatterns, - overlays: coreStart.overlays, - }); - }, - }); - } - - start(core: CoreStart, { npData, navigation }: GraphPluginStartDependencies) { - this.navigationStart = navigation; - this.npDataStart = npData; - this.savedObjectsClient = core.savedObjects.client; - } - - stop() {} -} diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js index 650e827cc16562..9af1a135794c0d 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js @@ -10,10 +10,8 @@ import { Provider } from 'react-redux'; import { render, unmountComponentAtNode } from 'react-dom'; import 'mapbox-gl/dist/mapbox-gl.css'; -import { - Embeddable, - APPLY_FILTER_TRIGGER, -} from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { Embeddable } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/ui_actions/public'; import { esFilters } from '../../../../../../src/plugins/data/public'; import { I18nContext } from 'ui/i18n'; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts new file mode 100644 index 00000000000000..c048510c50c365 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts @@ -0,0 +1,155 @@ +/* + * 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 { + NUMBER_OF_SIGNALS, + OPEN_CLOSE_SIGNALS_BTN, + SELECTED_SIGNALS, + SHOWING_SIGNALS, + SIGNALS, +} from '../screens/detections'; + +import { + closeFirstSignal, + closeSignals, + goToClosedSignals, + goToOpenedSignals, + openSignals, + selectNumberOfSignals, + waitForSignalsPanelToBeLoaded, + waitForSignals, + waitForSignalsToBeLoaded, +} from '../tasks/detections'; +import { esArchiverLoad } from '../tasks/es_archiver'; +import { loginAndWaitForPage } from '../tasks/login'; + +import { DETECTIONS } from '../urls/navigation'; + +describe('Detections', () => { + beforeEach(() => { + esArchiverLoad('signals'); + loginAndWaitForPage(DETECTIONS); + }); + + it('Closes and opens signals', () => { + waitForSignalsPanelToBeLoaded(); + waitForSignalsToBeLoaded(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .then(numberOfSignals => { + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignals} signals`); + + const numberOfSignalsToBeClosed = 3; + selectNumberOfSignals(numberOfSignalsToBeClosed); + + cy.get(SELECTED_SIGNALS) + .invoke('text') + .should('eql', `Selected ${numberOfSignalsToBeClosed} signals`); + + closeSignals(); + waitForSignals(); + cy.reload(); + waitForSignals(); + + const expectedNumberOfSignalsAfterClosing = +numberOfSignals - numberOfSignalsToBeClosed; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eq', expectedNumberOfSignalsAfterClosing.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfSignalsAfterClosing.toString()} signals`); + + goToClosedSignals(); + waitForSignals(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', numberOfSignalsToBeClosed.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signals`); + cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); + + const numberOfSignalsToBeOpened = 1; + selectNumberOfSignals(numberOfSignalsToBeOpened); + + cy.get(SELECTED_SIGNALS) + .invoke('text') + .should('eql', `Selected ${numberOfSignalsToBeOpened} signal`); + + openSignals(); + waitForSignals(); + cy.reload(); + waitForSignalsToBeLoaded(); + waitForSignals(); + goToClosedSignals(); + waitForSignals(); + + const expectedNumberOfClosedSignalsAfterOpened = 2; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', expectedNumberOfClosedSignalsAfterOpened.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfClosedSignalsAfterOpened.toString()} signals`); + cy.get(SIGNALS).should('have.length', expectedNumberOfClosedSignalsAfterOpened); + + goToOpenedSignals(); + waitForSignals(); + + const expectedNumberOfOpenedSignals = + +numberOfSignals - expectedNumberOfClosedSignalsAfterOpened; + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfOpenedSignals.toString()} signals`); + + cy.get('[data-test-subj="server-side-event-count"]') + .invoke('text') + .should('eql', expectedNumberOfOpenedSignals.toString()); + }); + }); + + it('Closes one signal when more than one opened signals are selected', () => { + waitForSignalsToBeLoaded(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .then(numberOfSignals => { + const numberOfSignalsToBeClosed = 1; + const numberOfSignalsToBeSelected = 3; + + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('have.attr', 'disabled'); + selectNumberOfSignals(numberOfSignalsToBeSelected); + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('not.have.attr', 'disabled'); + + closeFirstSignal(); + cy.reload(); + waitForSignalsToBeLoaded(); + waitForSignals(); + + const expectedNumberOfSignals = +numberOfSignals - numberOfSignalsToBeClosed; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eq', expectedNumberOfSignals.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfSignals.toString()} signals`); + + goToClosedSignals(); + waitForSignals(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', numberOfSignalsToBeClosed.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signal`); + cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index 8089b028a10d48..f388ac1215d01f 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -4,6 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ +export const CLOSED_SIGNALS_BTN = '[data-test-subj="closedSignals"]'; + export const LOADING_SIGNALS_PANEL = '[data-test-subj="loading-signals-panel"]'; export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal-detection-rules"]'; + +export const NUMBER_OF_SIGNALS = '[data-test-subj="server-side-event-count"]'; + +export const OPEN_CLOSE_SIGNAL_BTN = '[data-test-subj="update-signal-status-button"]'; + +export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] button'; + +export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; + +export const SELECTED_SIGNALS = '[data-test-subj="selectedSignals"]'; + +export const SHOWING_SIGNALS = '[data-test-subj="showingSignals"]'; + +export const SIGNALS = '[data-test-subj="event"]'; + +export const SIGNAL_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index 4a0a565a74e27d..3416e3eb81de33 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -4,7 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN } from '../screens/detections'; +import { + CLOSED_SIGNALS_BTN, + LOADING_SIGNALS_PANEL, + MANAGE_SIGNAL_DETECTION_RULES_BTN, + OPEN_CLOSE_SIGNAL_BTN, + OPEN_CLOSE_SIGNALS_BTN, + OPENED_SIGNALS_BTN, + SIGNALS, + SIGNAL_CHECKBOX, +} from '../screens/detections'; +import { REFRESH_BUTTON } from '../screens/siem_header'; + +export const closeFirstSignal = () => { + cy.get(OPEN_CLOSE_SIGNAL_BTN) + .first() + .click({ force: true }); +}; + +export const closeSignals = () => { + cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); +}; + +export const goToClosedSignals = () => { + cy.get(CLOSED_SIGNALS_BTN).click({ force: true }); +}; export const goToManageSignalDetectionRules = () => { cy.get(MANAGE_SIGNAL_DETECTION_RULES_BTN) @@ -12,6 +36,28 @@ export const goToManageSignalDetectionRules = () => { .click({ force: true }); }; +export const goToOpenedSignals = () => { + cy.get(OPENED_SIGNALS_BTN).click({ force: true }); +}; + +export const openSignals = () => { + cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); +}; + +export const selectNumberOfSignals = (numberOfSignals: number) => { + for (let i = 0; i < numberOfSignals; i++) { + cy.get(SIGNAL_CHECKBOX) + .eq(i) + .click({ force: true }); + } +}; + +export const waitForSignals = () => { + cy.get(REFRESH_BUTTON) + .invoke('text') + .should('not.equal', 'Updating'); +}; + export const waitForSignalsIndexToBeCreated = () => { cy.request({ url: '/api/detection_engine/index', retryOnStatusCodeFailure: true }).then( response => { @@ -26,3 +72,8 @@ export const waitForSignalsPanelToBeLoaded = () => { cy.get(LOADING_SIGNALS_PANEL).should('exist'); cy.get(LOADING_SIGNALS_PANEL).should('not.exist'); }; + +export const waitForSignalsToBeLoaded = () => { + const expectedNumberOfDisplayedSignals = 25; + cy.get(SIGNALS).should('have.length', expectedNumberOfDisplayedSignals); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts index 72c95cba2361b0..1743fcb5610646 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts @@ -12,6 +12,14 @@ export const esArchiverLoadEmptyKibana = () => { ); }; +export const esArchiverLoad = (folder: string) => { + cy.exec( + `node ../../../../scripts/es_archiver load ${folder} --dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( + 'ELASTICSEARCH_URL' + )} --kibana-url ${Cypress.config().baseUrl}` + ); +}; + export const esArchiverUnloadEmptyKibana = () => { cy.exec( `node ../../../../scripts/es_archiver empty_kibana unload empty--dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( diff --git a/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx b/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx index d3e2be0e8f8163..19e884e326390c 100644 --- a/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx +++ b/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx @@ -42,11 +42,23 @@ Popover.displayName = 'Popover'; export interface UtilityBarActionProps extends LinkIconProps { popoverContent?: (closePopover: () => void) => React.ReactNode; + dataTestSubj?: string; } export const UtilityBarAction = React.memo( - ({ children, color, disabled, href, iconSide, iconSize, iconType, onClick, popoverContent }) => ( - + ({ + children, + color, + dataTestSubj, + disabled, + href, + iconSide, + iconSize, + iconType, + onClick, + popoverContent, + }) => ( + {popoverContent ? ( (({ children }) => ( - {children} +export const UtilityBarText = React.memo(({ children, dataTestSubj }) => ( + {children} )); UtilityBarText.displayName = 'UtilityBarText'; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx index 1349246494ec81..7b655999ace09c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx @@ -8,7 +8,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiBasicTable, EuiButton, - EuiButtonIcon, EuiContextMenuPanel, EuiEmptyPrompt, EuiFlexGroup, @@ -45,6 +44,9 @@ import { OpenClosedStats } from '../open_closed_stats'; import { getActions } from './actions'; import { CasesTableFilters } from './table_filters'; +const CONFIGURE_CASES_URL = getConfigureCasesUrl(); +const CREATE_CASE_URL = getCreateCaseUrl(); + const Div = styled.div` margin-top: ${({ theme }) => theme.eui.paddingSizes.m}; `; @@ -259,16 +261,14 @@ export const AllCases = React.memo(() => { /> - - {i18n.CREATE_TITLE} + + {i18n.CONFIGURE_CASES_BUTTON} - + + {i18n.CREATE_TITLE} + @@ -325,7 +325,7 @@ export const AllCases = React.memo(() => { titleSize="xs" body={i18n.NO_CASES_BODY} actions={ - + {i18n.ADD_NEW_CASE} } diff --git a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts index 9c0287a56ccbcd..6ef412d408ae5d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts @@ -120,7 +120,7 @@ export const CONFIGURE_CASES_PAGE_TITLE = i18n.translate( ); export const CONFIGURE_CASES_BUTTON = i18n.translate('xpack.siem.case.configureCasesButton', { - defaultMessage: 'Configure cases', + defaultMessage: 'Edit third-party connection', }); export const ADD_COMMENT = i18n.translate('xpack.siem.case.caseView.comment.addComment', { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx index 3c1317d463f8e4..a8dd22863e3c9d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx @@ -32,6 +32,7 @@ const SignalsTableFilterGroupComponent: React.FC = ({ onFilterGroupChange return ( = ({ onFilterGroupChange diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx index 25c0424cadf113..2000a699ab18d2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx @@ -65,13 +65,15 @@ const SignalsUtilityBarComponent: React.FC = ({ - {i18n.SHOWING_SIGNALS(formattedTotalCount, totalCount)} + + {i18n.SHOWING_SIGNALS(formattedTotalCount, totalCount)} + {canUserCRUD && hasIndexWrite && ( <> - + {i18n.SELECTED_SIGNALS( showClearSelection ? formattedTotalCount : formattedSelectedEventsCount, showClearSelection ? totalCount : Object.keys(selectedEventIds).length @@ -79,6 +81,7 @@ const SignalsUtilityBarComponent: React.FC = ({ ; export type CheckGeo = t.TypeOf; export type Location = t.TypeOf; +export type StatesIndexStatus = t.TypeOf; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx new file mode 100644 index 00000000000000..cac7042ca5b5c6 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx @@ -0,0 +1,30 @@ +/* + * 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 React, { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { indexStatusAction } from '../../../state/actions'; +import { indexStatusSelector } from '../../../state/selectors'; +import { EmptyStateComponent } from '../../functional/empty_state/empty_state'; + +export const EmptyState: React.FC = ({ children }) => { + const { data, loading, errors } = useSelector(indexStatusSelector); + + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(indexStatusAction.get()); + }, [dispatch]); + + return ( + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts index 2e30e5c3cb24f1..baa961ddc87d25 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts @@ -13,3 +13,4 @@ export { MonitorStatusBar } from './monitor/status_bar_container'; export { MonitorListDrawer } from './monitor/list_drawer_container'; export { MonitorListActionsPopover } from './monitor/drawer_popover_container'; export { DurationChart } from './charts/monitor_duration'; +export { EmptyState } from './empty_state/empty_state'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx index d0f160b2c55403..a42f96962b95e4 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx @@ -10,7 +10,7 @@ import { selectIndexPattern } from '../../../state/selectors'; import { getIndexPattern } from '../../../state/actions'; import { KueryBarComponent } from '../../functional'; -const mapStateToProps = (state: AppState) => ({ indexPattern: selectIndexPattern(state) }); +const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); const mapDispatchToProps = (dispatch: any) => ({ loadIndexPattern: () => { diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx index cbd1fae77c5183..79aaa071507e18 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx @@ -18,6 +18,6 @@ const mapDispatchToProps = (dispatch: any): DispatchProps => ({ setEsKueryFilters: (esFilters: string) => dispatch(setEsKueryString(esFilters)), }); -const mapStateToProps = (state: AppState) => ({ indexPattern: selectIndexPattern(state) }); +const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); export const OverviewPage = connect(mapStateToProps, mapDispatchToProps)(OverviewPageComponent); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap index 472d9c2be59e4c..a885cfe22ccd25 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap @@ -2,16 +2,6 @@ exports[`EmptyState component does not render empty state with appropriate base path and no docs 1`] = ` `; -exports[`EmptyState component doesn't render child components when count is falsey 1`] = ` +exports[`EmptyState component doesn't render child components when count is falsy 1`] = ` { let statesIndexStatus: StatesIndexStatus; @@ -16,15 +16,13 @@ describe('EmptyState component', () => { beforeEach(() => { statesIndexStatus = { indexExists: true, - docCount: { - count: 1, - }, + docCount: 1, }; }); it('renders child components when count is truthy', () => { const component = shallowWithIntl( - +
Foo
Bar
Baz
@@ -33,9 +31,9 @@ describe('EmptyState component', () => { expect(component).toMatchSnapshot(); }); - it(`doesn't render child components when count is falsey`, () => { + it(`doesn't render child components when count is falsy`, () => { const component = mountWithIntl( - +
Shouldn't be rendered
); @@ -57,7 +55,7 @@ describe('EmptyState component', () => { }, ]; const component = mountWithIntl( - +
Shouldn't appear...
); @@ -66,7 +64,7 @@ describe('EmptyState component', () => { it('renders loading state if no errors or doc count', () => { const component = mountWithIntl( - +
Should appear even while loading...
); @@ -75,13 +73,11 @@ describe('EmptyState component', () => { it('does not render empty state with appropriate base path and no docs', () => { statesIndexStatus = { - docCount: { - count: 0, - }, + docCount: 0, indexExists: true, }; const component = mountWithIntl( - +
If this is in the snapshot the test should fail
); @@ -91,7 +87,7 @@ describe('EmptyState component', () => { it('notifies when index does not exist', () => { statesIndexStatus.indexExists = false; const component = mountWithIntl( - +
This text should not render
); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx index d2d46dff3b9f52..80afc2894ea441 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx @@ -6,29 +6,29 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; -import { UptimeGraphQLQueryProps, withUptimeGraphQL } from '../../higher_order'; -import { docCountQuery } from '../../../queries'; import { EmptyStateError } from './empty_state_error'; import { EmptyStateLoading } from './empty_state_loading'; -import { StatesIndexStatus } from '../../../../common/graphql/types'; import { DataMissing } from './data_missing'; - -interface EmptyStateQueryResult { - statesIndexStatus?: StatesIndexStatus; -} +import { StatesIndexStatus } from '../../../../common/runtime_types'; interface EmptyStateProps { children: JSX.Element[] | JSX.Element; + statesIndexStatus: StatesIndexStatus | null; + loading: boolean; + errors?: Error[]; } -type Props = UptimeGraphQLQueryProps & EmptyStateProps; - -export const EmptyStateComponent = ({ children, data, errors }: Props) => { - if (errors) { +export const EmptyStateComponent = ({ + children, + statesIndexStatus, + loading, + errors, +}: EmptyStateProps) => { + if (errors?.length) { return ; } - if (data && data.statesIndexStatus) { - const { indexExists, docCount } = data.statesIndexStatus; + if (!loading && statesIndexStatus) { + const { indexExists, docCount } = statesIndexStatus; if (!indexExists) { return ( { })} /> ); - } else if (indexExists && docCount && docCount.count === 0) { + } else if (indexExists && docCount === 0) { return ( { } return ; }; - -export const EmptyState = withUptimeGraphQL( - EmptyStateComponent, - docCountQuery -); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx index 745b185b57facb..c8e2bece1cb7fa 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx @@ -7,15 +7,14 @@ import { EuiEmptyPrompt, EuiPanel, EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; -import { GraphQLError } from 'graphql'; interface EmptyStateErrorProps { - errors: GraphQLError[]; + errors: Error[]; } export const EmptyStateError = ({ errors }: EmptyStateErrorProps) => { const unauthorized = errors.find( - (error: GraphQLError) => error.message && error.message.includes('unauthorized') + (error: Error) => error.message && error.message.includes('unauthorized') ); return ( @@ -46,7 +45,7 @@ export const EmptyStateError = ({ errors }: EmptyStateErrorProps) => { body={ {!unauthorized && - errors.map((error: GraphQLError) =>

{error.message}

)} + errors.map((error: Error) =>

{error.message}

)}
} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts index e86ba548fb5d96..daba13d8df6418 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts @@ -5,7 +5,6 @@ */ export { DonutChart } from './charts/donut_chart'; -export { EmptyState } from './empty_state'; export { KueryBarComponent } from './kuery_bar/kuery_bar'; export { MonitorCharts } from './monitor_charts'; export { MonitorList } from './monitor_list'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx index 496e8d898df3c8..2f5ccc2adf313a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx @@ -34,14 +34,16 @@ function convertKueryToEsQuery(kuery: string, indexPattern: IIndexPattern) { interface Props { autocomplete: DataPublicPluginSetup['autocomplete']; - loadIndexPattern: any; - indexPattern: any; + loadIndexPattern: () => void; + indexPattern: IIndexPattern | null; + loading: boolean; } export function KueryBarComponent({ autocomplete: autocompleteService, loadIndexPattern, indexPattern, + loading, }: Props) { useEffect(() => { if (!indexPattern) { @@ -53,19 +55,13 @@ export function KueryBarComponent({ suggestions: [], isLoadingIndexPattern: true, }); - const [isLoadingIndexPattern, setIsLoadingIndexPattern] = useState(true); const [isLoadingSuggestions, setIsLoadingSuggestions] = useState(false); let currentRequestCheck: string; - useEffect(() => { - if (indexPattern !== undefined) { - setIsLoadingIndexPattern(false); - } - }, [indexPattern]); const [getUrlParams, updateUrlParams] = useUrlParams(); const { search: kuery } = getUrlParams(); - const indexPatternMissing = !isLoadingIndexPattern && !indexPattern; + const indexPatternMissing = loading && !indexPattern; async function onChange(inputValue: string, selectionStart: number) { if (!indexPattern) { @@ -124,7 +120,7 @@ export function KueryBarComponent({ { }, }, ], - totalSummaryCount: { - count: 2, - }, + totalSummaryCount: 2, }; }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx index ff54e61006156b..1aef9281a3066e 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx @@ -89,9 +89,7 @@ describe('MonitorListPagination component', () => { }, }, ], - totalSummaryCount: { - count: 2, - }, + totalSummaryCount: 2, }; }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json index a45e974685b9c9..e8142f0480c4ab 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json @@ -3,7 +3,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": null, - "totalSummaryCount": { "count": 147428, "__typename": "DocCount" }, + "totalSummaryCount": 147428, "summaries": [ { "monitor_id": "andrewvc-com", diff --git a/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts b/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts index 5fcacf84246603..ab4d6f75849e88 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts @@ -24,7 +24,7 @@ const getKueryString = (urlFilters: string): string => { }; export const useUpdateKueryString = ( - indexPattern: IIndexPattern, + indexPattern: IIndexPattern | null, filterQueryString = '', urlFilters: string ): [string?, Error?] => { diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx index 15e31d5e446291..af9b8bf0464169 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx @@ -9,7 +9,6 @@ import React, { useContext, useEffect } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { - EmptyState, MonitorList, OverviewPageParsingErrorCallout, StatusPanel, @@ -19,13 +18,13 @@ import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { useTrackPageview } from '../../../../../plugins/observability/public'; import { DataPublicPluginSetup, IIndexPattern } from '../../../../../../src/plugins/data/public'; import { UptimeThemeContext } from '../contexts'; -import { FilterGroup, KueryBar } from '../components/connected'; +import { EmptyState, FilterGroup, KueryBar } from '../components/connected'; import { useUpdateKueryString } from '../hooks'; import { PageHeader } from './page_header'; interface OverviewPageProps { autocomplete: DataPublicPluginSetup['autocomplete']; - indexPattern: IIndexPattern; + indexPattern: IIndexPattern | null; setEsKueryFilters: (esFilters: string) => void; } @@ -81,7 +80,7 @@ export const OverviewPageComponent = ({ autocomplete, indexPattern, setEsKueryFi return ( <> - + diff --git a/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts b/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts deleted file mode 100644 index 3067a9d16f050c..00000000000000 --- a/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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 gql from 'graphql-tag'; - -export const docCountQueryString = ` -query GetStateIndexStatus { - statesIndexStatus: getStatesIndexStatus { - docCount { - count - } - indexExists - } -} -`; - -export const docCountQuery = gql` - ${docCountQueryString} -`; diff --git a/x-pack/legacy/plugins/uptime/public/queries/index.ts b/x-pack/legacy/plugins/uptime/public/queries/index.ts index f2fff9bc506d00..283382ec1b7baa 100644 --- a/x-pack/legacy/plugins/uptime/public/queries/index.ts +++ b/x-pack/legacy/plugins/uptime/public/queries/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { docCountQuery, docCountQueryString } from './doc_count_query'; export { pingsQuery, pingsQueryString } from './pings_query'; diff --git a/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts b/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts index 76f62ad453bd91..9e609786094d59 100644 --- a/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts +++ b/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts @@ -17,9 +17,7 @@ query MonitorStates($dateRangeStart: String!, $dateRangeEnd: String!, $paginatio ) { prevPagePagination nextPagePagination - totalSummaryCount { - count - } + totalSummaryCount summaries { monitor_id histogram { diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index.ts b/x-pack/legacy/plugins/uptime/public/state/actions/index.ts index dfcea64bf9c088..b2ab73879a4a75 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/index.ts @@ -11,3 +11,4 @@ export * from './monitor_status'; export * from './index_patternts'; export * from './ping'; export * from './monitor_duration'; +export * from './index_status'; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts new file mode 100644 index 00000000000000..336758a71ce60e --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts @@ -0,0 +1,10 @@ +/* + * 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 { createAsyncAction } from './utils'; +import { StatesIndexStatus } from '../../../common/runtime_types'; + +export const indexStatusAction = createAsyncAction('GET INDEX STATUS'); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/types.ts b/x-pack/legacy/plugins/uptime/public/state/actions/types.ts index dba70ed839ac57..e9bf11256b0b8b 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/types.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/types.ts @@ -4,6 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Action } from 'redux-actions'; + +export interface AsyncAction { + get: (payload?: any) => Action; + success: (payload?: any) => Action; + fail: (payload?: any) => Action; +} + export interface QueryParams { monitorId: string; dateStart: string; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts b/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts new file mode 100644 index 00000000000000..337c4bfb2fa470 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts @@ -0,0 +1,16 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { AsyncAction } from './types'; + +export function createAsyncAction(actionStr: string): AsyncAction { + return { + get: createAction(actionStr), + success: createAction(`${actionStr}_SUCCESS`), + fail: createAction(`${actionStr}_FAIL`), + }; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index.ts b/x-pack/legacy/plugins/uptime/public/state/api/index.ts index 7d42c6ee46bdc9..518091cb36dded 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/index.ts @@ -9,5 +9,6 @@ export * from './overview_filters'; export * from './snapshot'; export * from './monitor_status'; export * from './index_pattern'; +export * from './index_status'; export * from './ping'; export * from './monitor_duration'; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts new file mode 100644 index 00000000000000..9c531b3406a7c8 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts @@ -0,0 +1,31 @@ +/* + * 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 { PathReporter } from 'io-ts/lib/PathReporter'; +import { isRight } from 'fp-ts/lib/Either'; +import { getApiPath } from '../../lib/helper'; +import { REST_API_URLS } from '../../../common/constants/rest_api'; +import { StatesIndexStatus, StatesIndexStatusType } from '../../../common/runtime_types'; + +interface ApiRequest { + basePath: string; +} + +export const fetchIndexStatus = async ({ basePath }: ApiRequest): Promise => { + const url = getApiPath(REST_API_URLS.INDEX_STATUS, basePath); + + const response = await fetch(url); + if (!response.ok) { + throw new Error(response.statusText); + } + const responseData = await response.json(); + const decoded = StatesIndexStatusType.decode(responseData); + PathReporter.report(decoded); + if (isRight(decoded)) { + return decoded.right; + } + throw PathReporter.report(decoded); +}; diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts index d293cdbe451b54..ea389ff0a67458 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts @@ -26,10 +26,6 @@ export function fetchEffectFactory( ) { return function*(action: Action) { try { - if (!action.payload) { - yield put(fail(new Error('Cannot fetch snapshot for undefined parameters.'))); - return; - } const { payload: { ...params }, } = action; diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index.ts b/x-pack/legacy/plugins/uptime/public/state/effects/index.ts index 43af88f4cc291b..7c45aa142ecfda 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/index.ts @@ -12,6 +12,7 @@ import { fetchMonitorStatusEffect } from './monitor_status'; import { fetchIndexPatternEffect } from './index_pattern'; import { fetchPingHistogramEffect } from './ping'; import { fetchMonitorDurationEffect } from './monitor_duration'; +import { fetchIndexStatusEffect } from './index_status'; export function* rootEffect() { yield fork(fetchMonitorDetailsEffect); @@ -21,4 +22,5 @@ export function* rootEffect() { yield fork(fetchIndexPatternEffect); yield fork(fetchPingHistogramEffect); yield fork(fetchMonitorDurationEffect); + yield fork(fetchIndexStatusEffect); } diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts new file mode 100644 index 00000000000000..793a671f5fed8b --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts @@ -0,0 +1,17 @@ +/* + * 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 { takeLatest } from 'redux-saga/effects'; +import { indexStatusAction } from '../actions'; +import { fetchIndexStatus } from '../api'; +import { fetchEffectFactory } from './fetch_effect'; + +export function* fetchIndexStatusEffect() { + yield takeLatest( + indexStatusAction.get, + fetchEffectFactory(fetchIndexStatus, indexStatusAction.success, indexStatusAction.fail) + ); +} diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts index 32362afae42bc6..4a83b54504ca87 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts @@ -13,6 +13,7 @@ import { monitorStatusReducer } from './monitor_status'; import { indexPatternReducer } from './index_pattern'; import { pingReducer } from './ping'; import { monitorDurationReducer } from './monitor_duration'; +import { indexStatusReducer } from './index_status'; export const rootReducer = combineReducers({ monitor: monitorReducer, @@ -23,4 +24,5 @@ export const rootReducer = combineReducers({ indexPattern: indexPatternReducer, ping: pingReducer, monitorDuration: monitorDurationReducer, + indexStatus: indexStatusReducer, }); diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts index dff043f81b95c2..bc482e2f35c451 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts @@ -5,9 +5,10 @@ */ import { handleActions, Action } from 'redux-actions'; import { getIndexPattern, getIndexPatternSuccess, getIndexPatternFail } from '../actions'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; export interface IndexPatternState { - index_pattern: any; + index_pattern: IIndexPattern | null; errors: any[]; loading: boolean; } diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts new file mode 100644 index 00000000000000..50a02210fb5d3c --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts @@ -0,0 +1,30 @@ +/* + * 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 { handleActions } from 'redux-actions'; +import { indexStatusAction } from '../actions'; +import { handleAsyncAction } from './utils'; +import { IReducerState } from './types'; +import { StatesIndexStatus } from '../../../common/runtime_types'; + +export interface IndexStatusState extends IReducerState { + data: StatesIndexStatus | null; +} + +const initialState: IndexStatusState = { + data: null, + loading: false, + errors: [], +}; + +type PayLoad = StatesIndexStatus & Error; + +export const indexStatusReducer = handleActions( + { + ...handleAsyncAction('data', indexStatusAction), + }, + initialState +); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/types.ts similarity index 76% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts rename to x-pack/legacy/plugins/uptime/public/state/reducers/types.ts index 8ee70bf51f0060..40fe4bddbf1723 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/types.ts @@ -4,4 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { EmptyState } from './empty_state'; +export interface IReducerState { + errors: Error[]; + loading: boolean; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts new file mode 100644 index 00000000000000..773ec10686943f --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts @@ -0,0 +1,33 @@ +/* + * 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 { Action } from 'redux-actions'; +import { AsyncAction } from '../actions/types'; +import { IReducerState } from './types'; + +export function handleAsyncAction( + storeKey: string, + asyncAction: AsyncAction +) { + return { + [String(asyncAction.get)]: (state: ReducerState) => ({ + ...state, + loading: true, + }), + + [String(asyncAction.success)]: (state: ReducerState, action: Action) => ({ + ...state, + loading: false, + [storeKey]: action.payload === null ? action.payload : { ...action.payload }, + }), + + [String(asyncAction.fail)]: (state: ReducerState, action: Action) => ({ + ...state, + errors: [...state.errors, action.payload], + loading: false, + }), + }; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts index 24d34b4d067cc0..de446418632b8d 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts @@ -60,6 +60,11 @@ describe('state selectors', () => { loading: false, errors: [], }, + indexStatus: { + loading: false, + data: null, + errors: [], + }, }; it('selects base path from state', () => { diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts index 0a914a14c372be..adba288b8b145a 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts @@ -30,7 +30,7 @@ export const selectMonitorStatus = (state: AppState) => { }; export const selectIndexPattern = ({ indexPattern }: AppState) => { - return indexPattern.index_pattern; + return { indexPattern: indexPattern.index_pattern, loading: indexPattern.loading }; }; export const selectPingHistogram = ({ ping, ui }: AppState) => { @@ -45,3 +45,7 @@ export const selectPingHistogram = ({ ping, ui }: AppState) => { export const selectDurationLines = ({ monitorDuration }: AppState) => { return monitorDuration; }; + +export const indexStatusSelector = ({ indexStatus }: AppState) => { + return indexStatus; +}; diff --git a/x-pack/legacy/plugins/graph/README.md b/x-pack/plugins/graph/README.md similarity index 91% rename from x-pack/legacy/plugins/graph/README.md rename to x-pack/plugins/graph/README.md index f402b35bba49fe..9cc2617abe94c4 100644 --- a/x-pack/legacy/plugins/graph/README.md +++ b/x-pack/plugins/graph/README.md @@ -8,7 +8,7 @@ Graph shows only up in the side bar if your server is running on a platinum or t * Run tests `node x-pack/scripts/jest.js --watch plugins/graph` * Run type check `node scripts/type_check.js --project=x-pack/tsconfig.json` -* Run linter `node scripts/eslint.js x-pack/legacy/plugins/graph` +* Run linter `node scripts/eslint.js x-pack/plugins/graph` * Run functional tests (make sure to stop dev server) * Server `cd x-pack && node ./scripts/functional_tests_server.js` * Tests `cd x-pack && node ../scripts/functional_test_runner.js --config ./test/functional/config.js --grep=graph` @@ -21,7 +21,6 @@ Currently most of the state handling is done by a central angular controller. Th * `angular/` contains all code using javascript and angular. Rewriting this code in typescript and react is currently ongoing. When the migration is finished, this folder will go away * `components/` contains react components for various parts of the interface. Components can hold local UI state (e.g. current form data), everything else should be passed in from the caller. Styles should reside in a component-specific stylesheet -* `hacks/` contains files that need to run before the actual app is started. When moving to the new platform, this folder will go away. * `services/` contains functions that encapsule other parts of Kibana. Stateful dependencies are passed in from the outside. Components should not rely on services directly but have callbacks passed in. Once the migration to redux/saga is complete, only sagas will use services. * `helpers/` contains side effect free helper functions that can be imported and used from components and services * `state_management/` contains reducers, action creators, selectors and sagas. It also exports the central store creator diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index 0c77b446fa28dc..cf3a5fab92f56a 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["licensing"], + "requiredPlugins": ["licensing", "data", "navigation"], "optionalPlugins": ["home"], "configPath": ["xpack", "graph"] } diff --git a/x-pack/legacy/plugins/graph/public/_main.scss b/x-pack/plugins/graph/public/_main.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/_main.scss rename to x-pack/plugins/graph/public/_main.scss diff --git a/x-pack/legacy/plugins/graph/public/_mixins.scss b/x-pack/plugins/graph/public/_mixins.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/_mixins.scss rename to x-pack/plugins/graph/public/_mixins.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.d.ts b/x-pack/plugins/graph/public/angular/graph_client_workspace.d.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.d.ts rename to x-pack/plugins/graph/public/angular/graph_client_workspace.d.ts diff --git a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js b/x-pack/plugins/graph/public/angular/graph_client_workspace.js similarity index 85% rename from x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js rename to x-pack/plugins/graph/public/angular/graph_client_workspace.js index 14d4248dc664d2..a7d98a42404ecc 100644 --- a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js +++ b/x-pack/plugins/graph/public/angular/graph_client_workspace.js @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import $ from 'jquery'; + // Kibana wrapper const d3 = require('d3'); @@ -79,7 +81,7 @@ module.exports = (function() { self.redo = reverseOperation.undo; } - function GroupOperation(receiver, orphan, vm) { + function GroupOperation(receiver, orphan) { const self = this; self.receiver = receiver; self.orphan = orphan; @@ -91,7 +93,7 @@ module.exports = (function() { }; } - function UnGroupOperation(parent, child, vm) { + function UnGroupOperation(parent, child) { const self = this; self.parent = parent; self.child = child; @@ -152,9 +154,7 @@ module.exports = (function() { if (lastOps) { this.stopLayout(); this.redoLog.push(lastOps); - for (const i in lastOps) { - lastOps[i].undo(); - } + lastOps.forEach(ops => ops.undo()); this.runLayout(); } }; @@ -163,29 +163,22 @@ module.exports = (function() { if (lastOps) { this.stopLayout(); this.undoLog.push(lastOps); - for (const i in lastOps) { - lastOps[i].redo(); - } + lastOps.forEach(ops => ops.redo()); this.runLayout(); } }; //Determines if 2 nodes are connected via an edge this.areLinked = function(a, b) { - if (a == b) return true; - const allEdges = this.edges; - for (const e in allEdges) { - if (e.source == a) { - if (e.target == b) { - return true; - } + if (a === b) return true; + this.edges.forEach(e => { + if (e.source === a && e.target === b) { + return true; } - if (e.source == b) { - if (e.target == a) { - return true; - } + if (e.source === b && e.target === a) { + return true; } - } + }); return false; }; @@ -193,47 +186,43 @@ module.exports = (function() { this.selectAll = function() { self.selectedNodes = []; - for (const n in self.nodes) { - const node = self.nodes[n]; - if (node.parent == undefined) { + self.nodes.forEach(node => { + if (node.parent === undefined) { node.isSelected = true; self.selectedNodes.push(node); } else { node.isSelected = false; } - } + }); }; this.selectNone = function() { self.selectedNodes = []; - for (const n in self.nodes) { - const node = self.nodes[n]; + self.nodes.forEach(node => { node.isSelected = false; - } + }); }; this.selectInvert = function() { self.selectedNodes = []; - for (const n in self.nodes) { - const node = self.nodes[n]; - if (node.parent != undefined) { - continue; + self.nodes.forEach(node => { + if (node.parent !== undefined) { + return; } node.isSelected = !node.isSelected; if (node.isSelected) { self.selectedNodes.push(node); } - } + }); }; this.selectNodes = function(nodes) { - for (const n in nodes) { - const node = nodes[n]; + nodes.forEach(node => { node.isSelected = true; if (self.selectedNodes.indexOf(node) < 0) { self.selectedNodes.push(node); } - } + }); }; this.selectNode = function(node) { @@ -247,29 +236,27 @@ module.exports = (function() { let allAndGrouped = self.returnUnpackedGroupeds(self.selectedNodes); // Nothing selected so process all nodes - if (allAndGrouped.length == 0) { + if (allAndGrouped.length === 0) { allAndGrouped = self.nodes.slice(0); } const undoOperations = []; - for (const i in allAndGrouped) { - const node = allAndGrouped[i]; + allAndGrouped.forEach(node => { //We set selected to false because despite being deleted, node objects sit in an undo log node.isSelected = false; delete self.nodesMap[node.id]; undoOperations.push(new ReverseOperation(new AddNodeOperation(node, self))); - } + }); self.arrRemoveAll(self.nodes, allAndGrouped); self.arrRemoveAll(self.selectedNodes, allAndGrouped); const danglingEdges = self.edges.filter(function(edge) { return self.nodes.indexOf(edge.source) < 0 || self.nodes.indexOf(edge.target) < 0; }); - for (const i in danglingEdges) { - const edge = danglingEdges[i]; + danglingEdges.forEach(edge => { delete self.edgesMap[edge.id]; undoOperations.push(new ReverseOperation(new AddEdgeOperation(edge, self))); - } + }); self.addUndoLogEntry(undoOperations); self.arrRemoveAll(self.edges, danglingEdges); self.runLayout(); @@ -277,8 +264,7 @@ module.exports = (function() { this.selectNeighbours = function() { const newSelections = []; - for (const n in self.edges) { - const edge = self.edges[n]; + self.edges.forEach(edge => { if (!edge.topSrc.isSelected) { if (self.selectedNodes.indexOf(edge.topTarget) >= 0) { if (newSelections.indexOf(edge.topSrc) < 0) { @@ -293,18 +279,17 @@ module.exports = (function() { } } } - } - for (const i in newSelections) { - const newlySelectedNode = newSelections[i]; + }); + newSelections.forEach(newlySelectedNode => { self.selectedNodes.push(newlySelectedNode); newlySelectedNode.isSelected = true; - } + }); }; this.selectNone = function() { - for (const n in self.selectedNodes) { - self.selectedNodes[n].isSelected = false; - } + self.selectedNodes.forEach(node => { + node.isSelected = false; + }); self.selectedNodes = []; }; @@ -318,30 +303,25 @@ module.exports = (function() { }; this.colorSelected = function(colorNum) { - const selections = self.getAllSelectedNodes(); - for (const i in selections) { - selections[i].color = colorNum; - } + self.getAllSelectedNodes().forEach(node => { + node.color = colorNum; + }); }; this.getSelectionsThatAreGrouped = function() { const result = []; - const selections = self.selectedNodes; - for (const i in selections) { - const node = selections[i]; + self.selectedNodes.forEach(node => { if (node.numChildren > 0) { result.push(node); } - } + }); return result; }; this.ungroupSelection = function() { - const selections = self.getSelectionsThatAreGrouped(); - for (const i in selections) { - const node = selections[i]; + self.getSelectionsThatAreGrouped().forEach(node => { self.ungroup(node); - } + }); }; this.toggleNodeSelection = function(node) { @@ -371,7 +351,7 @@ module.exports = (function() { if (result.indexOf(topLevelTarget) >= 0) { //visible top-level node is selected - add all nesteds starting from bottom up let target = edge.target; - while (target.parent != undefined) { + while (target.parent !== undefined) { if (result.indexOf(target) < 0) { result.push(target); } @@ -382,7 +362,7 @@ module.exports = (function() { if (result.indexOf(topLevelSource) >= 0) { //visible top-level node is selected - add all nesteds starting from bottom up let source = edge.source; - while (source.parent != undefined) { + while (source.parent !== undefined) { if (result.indexOf(source) < 0) { result.push(source); } @@ -425,22 +405,21 @@ module.exports = (function() { this.getNeighbours = function(node) { const neighbourNodes = []; - for (const e in self.edges) { - const edge = self.edges[e]; - if (edge.topSrc == edge.topTarget) { - continue; + self.edges.forEach(edge => { + if (edge.topSrc === edge.topTarget) { + return; } - if (edge.topSrc == node) { + if (edge.topSrc === node) { if (neighbourNodes.indexOf(edge.topTarget) < 0) { neighbourNodes.push(edge.topTarget); } } - if (edge.topTarget == node) { + if (edge.topTarget === node) { if (neighbourNodes.indexOf(edge.topSrc) < 0) { neighbourNodes.push(edge.topSrc); } } - } + }); return neighbourNodes; }; @@ -448,7 +427,7 @@ module.exports = (function() { this.buildNodeQuery = function(topLevelNode) { let containedNodes = [topLevelNode]; containedNodes = self.returnUnpackedGroupeds(containedNodes); - if (containedNodes.length == 1) { + if (containedNodes.length === 1) { //Simple case - return a single-term query const tq = {}; tq[topLevelNode.data.field] = topLevelNode.data.term; @@ -457,17 +436,16 @@ module.exports = (function() { }; } const termsByField = {}; - for (const i in containedNodes) { - const node = containedNodes[i]; + containedNodes.forEach(node => { let termsList = termsByField[node.data.field]; if (!termsList) { termsList = []; termsByField[node.data.field] = termsList; } termsList.push(node.data.term); - } + }); //Single field case - if (Object.keys(termsByField).length == 1) { + if (Object.keys(termsByField).length === 1) { return { terms: termsByField, }; @@ -479,11 +457,13 @@ module.exports = (function() { }, }; for (const field in termsByField) { - const tq = {}; - tq[field] = termsByField[field]; - q.bool.should.push({ - terms: tq, - }); + if (termsByField.hasOwnProperty(field)) { + const tq = {}; + tq[field] = termsByField[field]; + q.bool.should.push({ + terms: tq, + }); + } } return q; }; @@ -503,39 +483,40 @@ module.exports = (function() { // is potentially a reduced set of nodes if the client has used any // grouping of nodes into parent nodes. const effectiveEdges = []; - const edges = self.edges; - for (const e in edges) { - const edge = edges[e]; + self.edges.forEach(edge => { let topSrc = edge.source; let topTarget = edge.target; - while (topSrc.parent != undefined) { + while (topSrc.parent !== undefined) { topSrc = topSrc.parent; } - while (topTarget.parent != undefined) { + while (topTarget.parent !== undefined) { topTarget = topTarget.parent; } edge.topSrc = topSrc; edge.topTarget = topTarget; - if (topSrc != topTarget) { + if (topSrc !== topTarget) { effectiveEdges.push({ source: topSrc, target: topTarget, }); } - } + }); const visibleNodes = self.nodes.filter(function(n) { - return n.parent == undefined; + return n.parent === undefined; }); //reset then roll-up all the counts const allNodes = self.nodes; - for (const n in allNodes) { - const node = allNodes[n]; + allNodes.forEach(node => { node.numChildren = 0; - } + }); + for (const n in allNodes) { + if (!allNodes.hasOwnProperty(n)) { + continue; + } let node = allNodes[n]; - while (node.parent != undefined) { + while (node.parent !== undefined) { node = node.parent; node.numChildren = node.numChildren + 1; } @@ -551,36 +532,34 @@ module.exports = (function() { .theta(0.99) .alpha(0.5) .size([800, 600]) - .on('tick', function(e) { + .on('tick', function() { const nodeArray = self.nodes; let hasRollups = false; //Update the position of all "top level nodes" - for (const i in nodeArray) { - const n = nodeArray[i]; + nodeArray.forEach(n => { //Code to support roll-ups - if (n.parent == undefined) { + if (n.parent === undefined) { n.kx = n.x; n.ky = n.y; } else { hasRollups = true; } - } + }); if (hasRollups) { - for (const i in nodeArray) { - const n = nodeArray[i]; + nodeArray.forEach(n => { //Code to support roll-ups - if (n.parent != undefined) { + if (n.parent !== undefined) { // Is a grouped node - inherit parent's position so edges point into parent // d3 thinks it has moved it to x and y but we have final say using kx and ky. let topLevelNode = n.parent; - while (topLevelNode.parent != undefined) { + while (topLevelNode.parent !== undefined) { topLevelNode = topLevelNode.parent; } n.kx = topLevelNode.x; n.ky = topLevelNode.y; } - } + }); } if (self.changeHandler) { // Hook to allow any client to respond to position changes @@ -597,11 +576,11 @@ module.exports = (function() { this.groupSelections = function(node) { const ops = []; self.nodes.forEach(function(otherNode) { - if (otherNode != node && otherNode.isSelected && otherNode.parent == undefined) { + if (otherNode !== node && otherNode.isSelected && otherNode.parent === undefined) { otherNode.parent = node; otherNode.isSelected = false; self.arrRemove(self.selectedNodes, otherNode); - ops.push(new GroupOperation(node, otherNode, self)); + ops.push(new GroupOperation(node, otherNode)); } }); self.selectNone(); @@ -614,11 +593,11 @@ module.exports = (function() { const neighbours = self.getNeighbours(node); const ops = []; neighbours.forEach(function(otherNode) { - if (otherNode != node && otherNode.parent == undefined) { + if (otherNode !== node && otherNode.parent === undefined) { otherNode.parent = node; otherNode.isSelected = false; self.arrRemove(self.selectedNodes, otherNode); - ops.push(new GroupOperation(node, otherNode, self)); + ops.push(new GroupOperation(node, otherNode)); } }); self.addUndoLogEntry(ops); @@ -633,11 +612,11 @@ module.exports = (function() { const selClone = self.selectedNodes.slice(); const ops = []; selClone.forEach(function(otherNode) { - if (otherNode != targetNode && otherNode.parent == undefined) { + if (otherNode !== targetNode && otherNode.parent === undefined) { otherNode.parent = targetNode; otherNode.isSelected = false; self.arrRemove(self.selectedNodes, otherNode); - ops.push(new GroupOperation(targetNode, otherNode, self)); + ops.push(new GroupOperation(targetNode, otherNode)); } }); self.addUndoLogEntry(ops); @@ -647,9 +626,9 @@ module.exports = (function() { this.ungroup = function(node) { const ops = []; self.nodes.forEach(function(other) { - if (other.parent == node) { + if (other.parent === node) { other.parent = undefined; - ops.push(new UnGroupOperation(node, other, self)); + ops.push(new UnGroupOperation(node, other)); } }); self.addUndoLogEntry(ops); @@ -669,12 +648,11 @@ module.exports = (function() { danglingEdges.push(edge); } }); - for (const n in selection) { - const node = selection[n]; + selection.forEach(node => { delete self.nodesMap[node.id]; self.blacklistedNodes.push(node); node.isSelected = false; - } + }); self.arrRemoveAll(self.nodes, selection); self.arrRemoveAll(self.edges, danglingEdges); self.selectedNodes = []; @@ -722,9 +700,7 @@ module.exports = (function() { for (let hopNum = 0; hopNum < numHops; hopNum++) { const arr = []; - for (const f in fieldsChoice) { - const field = fieldsChoice[f].name; - const hopSize = fieldsChoice[f].hopSize; + fieldsChoice.forEach(({ name: field, hopSize }) => { const excludes = excludeNodesByField[field]; const stepField = { field: field, @@ -735,7 +711,7 @@ module.exports = (function() { stepField.exclude = excludes; } arr.push(stepField); - } + }); step.vertices = arr; if (hopNum < numHops - 1) { // if (s < (stepSizes.length - 1)) { @@ -814,8 +790,7 @@ module.exports = (function() { //Remove nodes we already have const dedupedNodes = []; - for (const o in newData.nodes) { - const node = newData.nodes[o]; + newData.nodes.forEach(node => { //Assign an ID node.id = self.makeNodeId(node.field, node.term); if (!this.nodesMap[node.id]) { @@ -825,14 +800,13 @@ module.exports = (function() { } dedupedNodes.push(node); } - } + }); if (dedupedNodes.length > 0 && this.options.nodeLabeller) { // A hook for client code to attach labels etc to newly introduced nodes. this.options.nodeLabeller(dedupedNodes); } - for (const o in dedupedNodes) { - const dedupedNode = dedupedNodes[o]; + dedupedNodes.forEach(dedupedNode => { let label = dedupedNode.term; if (dedupedNode.label) { label = dedupedNode.label; @@ -856,10 +830,9 @@ module.exports = (function() { this.nodes.push(node); lastOps.push(new AddNodeOperation(node, self)); this.nodesMap[node.id] = node; - } + }); - for (const o in newData.edges) { - const edge = newData.edges[o]; + newData.edges.forEach(edge => { const src = newData.nodes[edge.source]; const target = newData.nodes[edge.target]; edge.id = this.makeEdgeId(src.id, target.id); @@ -873,7 +846,7 @@ module.exports = (function() { existingEdge.weight = Math.max(existingEdge.weight, edge.weight); //TODO update width too? existingEdge.doc_count = Math.max(existingEdge.doc_count, edge.doc_count); - continue; + return; } const newEdge = { source: srcWrapperObj, @@ -890,7 +863,7 @@ module.exports = (function() { this.edgesMap[newEdge.id] = newEdge; this.edges.push(newEdge); lastOps.push(new AddEdgeOperation(newEdge, self)); - } + }); if (lastOps.length > 0) { self.addUndoLogEntry(lastOps); @@ -907,7 +880,7 @@ module.exports = (function() { self.arrRemove(self.selectedNodes, child); } child.parent = parent; - self.addUndoLogEntry([new GroupOperation(parent, child, self)]); + self.addUndoLogEntry([new GroupOperation(parent, child)]); self.runLayout(); }; @@ -922,7 +895,7 @@ module.exports = (function() { this.expandSelecteds = function(targetOptions = {}) { let startNodes = self.getAllSelectedNodes(); - if (startNodes.length == 0) { + if (startNodes.length === 0) { startNodes = self.nodes; } const clone = startNodes.slice(); @@ -1000,11 +973,13 @@ module.exports = (function() { const primaryVertices = []; const secondaryVertices = []; for (const fieldName in nodesByField) { - primaryVertices.push({ - field: fieldName, - include: nodesByField[fieldName], - min_doc_count: parseInt(self.options.exploreControls.minDocCount), - }); + if (nodesByField.hasOwnProperty(fieldName)) { + primaryVertices.push({ + field: fieldName, + include: nodesByField[fieldName], + min_doc_count: parseInt(self.options.exploreControls.minDocCount), + }); + } } let targetFields = this.options.vertex_fields; @@ -1013,11 +988,11 @@ module.exports = (function() { } //Identify target fields - for (const f in targetFields) { - const fieldName = targetFields[f].name; + targetFields.forEach(targetField => { + const fieldName = targetField.name; // Sometimes the target field is disabled from loading new hops so we need to use the last valid figure const hopSize = - targetFields[f].hopSize > 0 ? targetFields[f].hopSize : targetFields[f].lastValidHopSize; + targetField.hopSize > 0 ? targetField.hopSize : targetField.lastValidHopSize; const fieldHop = { field: fieldName, @@ -1026,7 +1001,7 @@ module.exports = (function() { }; fieldHop.exclude = excludeNodesByField[fieldName]; secondaryVertices.push(fieldHop); - } + }); const request = { controls: self.buildControls(), @@ -1038,33 +1013,27 @@ module.exports = (function() { self.lastRequest = JSON.stringify(request, null, '\t'); graphExplorer(self.options.indexName, request, function(data) { self.lastResponse = JSON.stringify(data, null, '\t'); - const nodes = []; const edges = []; //Label fields with a field number for CSS styling - for (const n in data.vertices) { - const node = data.vertices[n]; - for (const f in targetFields) { - const fieldDef = targetFields[f]; - if (node.field == fieldDef.name) { + data.vertices.forEach(node => { + targetFields.some(fieldDef => { + if (node.field === fieldDef.name) { node.color = fieldDef.color; node.icon = fieldDef.icon; node.fieldDef = fieldDef; - break; + return true; } - } - } + return false; + }); + }); // Size the edges based on the maximum weight const minLineSize = 2; const maxLineSize = 10; let maxEdgeWeight = 0.00000001; - for (const e in data.connections) { - const edge = data.connections[e]; + data.connections.forEach(edge => { maxEdgeWeight = Math.max(maxEdgeWeight, edge.weight); - } - for (const e in data.connections) { - const edge = data.connections[e]; edges.push({ source: edge.source, target: edge.target, @@ -1072,7 +1041,7 @@ module.exports = (function() { weight: edge.weight, width: Math.max(minLineSize, (edge.weight / maxEdgeWeight) * maxLineSize), }); - } + }); // Add the new nodes and edges into the existing workspace's graph self.mergeGraph({ @@ -1087,8 +1056,7 @@ module.exports = (function() { let trimmedEdges = []; const maxNumEdgesToReturn = 5; //Trim here to just the new edges that are most interesting. - for (const o in newEdges) { - const edge = newEdges[o]; + newEdges.forEach(edge => { const src = newNodes[edge.source]; const target = newNodes[edge.target]; const srcId = src.field + '..' + src.term; @@ -1097,25 +1065,25 @@ module.exports = (function() { const existingSrcNode = self.nodesMap[srcId]; const existingTargetNode = self.nodesMap[targetId]; if (existingSrcNode != null && existingTargetNode != null) { - if (existingSrcNode.parent != undefined && existingTargetNode.parent != undefined) { + if (existingSrcNode.parent !== undefined && existingTargetNode.parent !== undefined) { // both nodes are rolled-up and grouped so this edge would not be a visible // change to the graph - lose it in favour of any other visible ones. - continue; + return; } } else { console.log('Error? Missing nodes ' + srcId + ' or ' + targetId, self.nodesMap); - continue; + return; } const existingEdge = self.edgesMap[id]; if (existingEdge) { existingEdge.weight = Math.max(existingEdge.weight, edge.weight); existingEdge.doc_count = Math.max(existingEdge.doc_count, edge.doc_count); - continue; + return; } else { trimmedEdges.push(edge); } - } + }); if (trimmedEdges.length > maxNumEdgesToReturn) { //trim to only the most interesting ones trimmedEdges.sort(function(a, b) { @@ -1132,12 +1100,11 @@ module.exports = (function() { if (!startNodes) { nodes = self.nodes; } - for (const bs in nodes) { - const node = nodes[bs]; - if (node.parent == undefined) { + nodes.forEach(node => { + if (node.parent === undefined) { shoulds.push(self.buildNodeQuery(node)); } - } + }); return { bool: { should: shoulds, @@ -1256,7 +1223,7 @@ module.exports = (function() { const t2 = keyedBuckets[ids[1]].doc_count; const t1AndT2 = bucket.doc_count; // Calc the significant_terms score to prioritize selection of interesting links - bucket.weight = self.JLHScore( + bucket.weight = self.jLHScore( t1AndT2, Math.max(t1, t2), Math.min(t1, t2), @@ -1276,7 +1243,7 @@ module.exports = (function() { return; } const ids = bucket.key.split('|'); - if (ids.length == 2) { + if (ids.length === 2) { // Bucket represents an edge const srcNode = nodesForLinking[ids[0]]; const targetNode = nodesForLinking[ids[1]]; @@ -1340,16 +1307,18 @@ module.exports = (function() { txtsByFieldType[node.data.field] = txt; }); for (const field in txtsByFieldType) { - likeQueries.push({ - more_like_this: { - like: txtsByFieldType[field], - min_term_freq: 1, - minimum_should_match: '20%', - min_doc_freq: 1, - boost_terms: 2, - max_query_terms: 25, - }, - }); + if (txtsByFieldType.hasOwnProperty(field)) { + likeQueries.push({ + more_like_this: { + like: txtsByFieldType[field], + min_term_freq: 1, + minimum_should_match: '20%', + min_doc_freq: 1, + boost_terms: 2, + max_query_terms: 25, + }, + }); + } } const excludeNodesByField = {}; @@ -1397,10 +1366,10 @@ module.exports = (function() { }; this.getSelectedIntersections = function(callback) { - if (self.selectedNodes.length == 0) { + if (self.selectedNodes.length === 0) { return self.getAllIntersections(callback, self.nodes); } - if (self.selectedNodes.length == 1) { + if (self.selectedNodes.length === 1) { const selectedNode = self.selectedNodes[0]; const neighbourNodes = self.getNeighbours(selectedNode); neighbourNodes.push(selectedNode); @@ -1409,7 +1378,7 @@ module.exports = (function() { return self.getAllIntersections(callback, self.getAllSelectedNodes()); }; - this.JLHScore = function(subsetFreq, subsetSize, supersetFreq, supersetSize) { + this.jLHScore = function(subsetFreq, subsetSize, supersetFreq, supersetSize) { const subsetProbability = subsetFreq / subsetSize; const supersetProbability = supersetFreq / supersetSize; @@ -1432,7 +1401,7 @@ module.exports = (function() { this.getAllIntersections = function(callback, nodes) { //Ensure these are all top-level nodes only nodes = nodes.filter(function(n) { - return n.parent == undefined; + return n.parent === undefined; }); const allQueries = nodes.map(function(node) { @@ -1468,44 +1437,42 @@ module.exports = (function() { }, }, }; - for (const n in allQueries) { + allQueries.forEach((query, n) => { // Add aggs to get intersection stats with root node. - request.aggs.sources.filters.filters['bg' + n] = allQueries[n]; - request.aggs.sources.aggs.targets.filters.filters['fg' + n] = allQueries[n]; - } - const dataForServer = JSON.stringify(request); + request.aggs.sources.filters.filters['bg' + n] = query; + request.aggs.sources.aggs.targets.filters.filters['fg' + n] = query; + }); searcher(self.options.indexName, request, function(data) { const termIntersects = []; const fullDocCounts = []; const allDocCount = data.aggregations.all.doc_count; // Gather the background stats for all nodes. - for (const n in nodes) { + nodes.forEach((rootNode, n) => { fullDocCounts.push(data.aggregations.sources.buckets['bg' + n].doc_count); - } - for (const n in nodes) { - const rootNode = nodes[n]; + }); + + nodes.forEach((rootNode, n) => { const t1 = fullDocCounts[n]; const baseAgg = data.aggregations.sources.buckets['bg' + n].targets.buckets; - for (const l in nodes) { + nodes.forEach((leafNode, l) => { const t2 = fullDocCounts[l]; - const leafNode = nodes[l]; - if (l == n) { - continue; + if (l === n) { + return; } if (t1 > t2) { // We should get the same stats for t2->t1 from the t1->t2 bucket path - continue; + return; } - if (t1 == t2) { + if (t1 === t2) { if (rootNode.id > leafNode.id) { // We should get the same stats for t2->t1 from the t1->t2 bucket path - continue; + return; } } const t1AndT2 = baseAgg['fg' + l].doc_count; - if (t1AndT2 == 0) { - continue; + if (t1AndT2 === 0) { + return; } const neighbourNode = nodes[l]; let t1Label = rootNode.data.label; @@ -1521,7 +1488,7 @@ module.exports = (function() { // var mergeConfidence=t1AndT2/t1; // So using Significance heuristic instead - const mergeConfidence = self.JLHScore(t1AndT2, t2, t1, allDocCount); + const mergeConfidence = self.jLHScore(t1AndT2, t2, t1, allDocCount); const termIntersect = { id1: rootNode.id, @@ -1536,16 +1503,16 @@ module.exports = (function() { overlap: t1AndT2, }; termIntersects.push(termIntersect); - } - } + }); + }); termIntersects.sort(function(a, b) { - if (b.mergeConfidence != a.mergeConfidence) { + if (b.mergeConfidence !== a.mergeConfidence) { return b.mergeConfidence - a.mergeConfidence; } // If of equal similarity use the size of the overlap as // a measure of magnitude/significance for tie-breaker. - if (b.overlap != a.overlap) { + if (b.overlap !== a.overlap) { return b.overlap - a.overlap; } //All other things being equal we now favour where t2 NOT t1 is small. @@ -1563,32 +1530,28 @@ module.exports = (function() { self.lastRequest = JSON.stringify(request, null, '\t'); graphExplorer(self.options.indexName, request, function(data) { self.lastResponse = JSON.stringify(data, null, '\t'); - const nodes = []; const edges = []; //Label the nodes with field number for CSS styling - for (const n in data.vertices) { - const node = data.vertices[n]; - for (const f in self.options.vertex_fields) { - const fieldDef = self.options.vertex_fields[f]; - if (node.field == fieldDef.name) { + data.vertices.forEach(node => { + self.options.vertex_fields.some(fieldDef => { + if (node.field === fieldDef.name) { node.color = fieldDef.color; node.icon = fieldDef.icon; node.fieldDef = fieldDef; - break; + return true; } - } - } + return false; + }); + }); //Size the edges depending on weight const minLineSize = 2; const maxLineSize = 10; let maxEdgeWeight = 0.00000001; - for (const e in data.connections) { - const edge = data.connections[e]; + data.connections.forEach(edge => { maxEdgeWeight = Math.max(maxEdgeWeight, edge.weight); - } - for (const e in data.connections) { - const edge = data.connections[e]; + }); + data.connections.forEach(edge => { edges.push({ source: edge.source, target: edge.target, @@ -1596,7 +1559,7 @@ module.exports = (function() { weight: edge.weight, width: Math.max(minLineSize, (edge.weight / maxEdgeWeight) * maxLineSize), }); - } + }); self.mergeGraph( { diff --git a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.test.js b/x-pack/plugins/graph/public/angular/graph_client_workspace.test.js similarity index 97% rename from x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.test.js rename to x-pack/plugins/graph/public/angular/graph_client_workspace.test.js index 6179467966764f..6f81a443086c05 100644 --- a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.test.js +++ b/x-pack/plugins/graph/public/angular/graph_client_workspace.test.js @@ -77,7 +77,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.nodes.length).toEqual(2); expect(workspace.edges.length).toEqual(1); @@ -119,7 +119,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.nodes.length).toEqual(2); expect(workspace.edges.length).toEqual(1); @@ -201,7 +201,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.selectedNodes.length).toEqual(0); @@ -264,7 +264,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.nodes.length).toEqual(2); @@ -320,7 +320,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.nodes.length).toEqual(2); diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/_graph.scss b/x-pack/plugins/graph/public/angular/templates/_graph.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/_graph.scss rename to x-pack/plugins/graph/public/angular/templates/_graph.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/_index.scss b/x-pack/plugins/graph/public/angular/templates/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/_index.scss rename to x-pack/plugins/graph/public/angular/templates/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/_inspect.scss b/x-pack/plugins/graph/public/angular/templates/_inspect.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/_inspect.scss rename to x-pack/plugins/graph/public/angular/templates/_inspect.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/_sidebar.scss b/x-pack/plugins/graph/public/angular/templates/_sidebar.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/_sidebar.scss rename to x-pack/plugins/graph/public/angular/templates/_sidebar.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/plugins/graph/public/angular/templates/index.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/index.html rename to x-pack/plugins/graph/public/angular/templates/index.html diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html b/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html rename to x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/plugins/graph/public/app.js similarity index 96% rename from x-pack/legacy/plugins/graph/public/app.js rename to x-pack/plugins/graph/public/app.js index df968681a38e22..72dddc2b9f813c 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/plugins/graph/public/app.js @@ -6,13 +6,12 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import 'ace'; import React from 'react'; import { Provider } from 'react-redux'; import { isColorDark, hexToRgb } from '@elastic/eui'; -import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; -import { showSaveModal } from '../../../../../src/plugins/saved_objects/public'; +import { toMountPoint } from '../../../../src/plugins/kibana_react/public'; +import { showSaveModal } from '../../../../src/plugins/saved_objects/public'; import appTemplate from './angular/templates/index.html'; import listingTemplate from './angular/templates/listing_ng_wrapper.html'; @@ -41,7 +40,7 @@ export function initGraphApp(angularModule, deps) { indexPatterns, addBasePath, getBasePath, - npData, + data, config, savedWorkspaceLoader, capabilities, @@ -107,7 +106,7 @@ export function initGraphApp(angularModule, deps) { .when('/home', { template: listingTemplate, badge: getReadonlyBadge, - controller($location, $scope) { + controller: function($location, $scope) { $scope.listingLimit = config.get('savedObjects:listingLimit'); $scope.create = () => { $location.url(getNewPath()); @@ -249,6 +248,7 @@ export function initGraphApp(angularModule, deps) { const store = createGraphStore({ basePath: getBasePath(), + addBasePath, indexPatternProvider: $scope.indexPatternProvider, indexPatterns: $route.current.locals.indexPatterns, createWorkspace: (indexPattern, exploreControls) => { @@ -301,7 +301,7 @@ export function initGraphApp(angularModule, deps) { }); // register things on scope passed down to react components - $scope.pluginDataStart = npData; + $scope.pluginDataStart = data; $scope.storage = storage; $scope.coreStart = coreStart; $scope.loading = false; @@ -420,11 +420,13 @@ export function initGraphApp(angularModule, deps) { while (found) { found = false; for (const i in $scope.detail.mergeCandidates) { - const mc = $scope.detail.mergeCandidates[i]; - if (mc.id1 === childId || mc.id2 === childId) { - $scope.detail.mergeCandidates.splice(i, 1); - found = true; - break; + if ($scope.detail.mergeCandidates.hasOwnProperty(i)) { + const mc = $scope.detail.mergeCandidates[i]; + if (mc.id1 === childId || mc.id2 === childId) { + $scope.detail.mergeCandidates.splice(i, 1); + found = true; + break; + } } } } @@ -434,8 +436,7 @@ export function initGraphApp(angularModule, deps) { $scope.handleMergeCandidatesCallback = function(termIntersects) { const mergeCandidates = []; - for (const i in termIntersects) { - const ti = termIntersects[i]; + termIntersects.forEach(ti => { mergeCandidates.push({ id1: ti.id1, id2: ti.id2, @@ -445,7 +446,7 @@ export function initGraphApp(angularModule, deps) { v2: ti.v2, overlap: ti.overlap, }); - } + }); $scope.detail = { mergeCandidates }; }; diff --git a/x-pack/legacy/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts similarity index 81% rename from x-pack/legacy/plugins/graph/public/application.ts rename to x-pack/plugins/graph/public/application.ts index 536382e62d4738..4f7bdd69db3560 100644 --- a/x-pack/legacy/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.ts @@ -9,34 +9,36 @@ // They can stay even after NP cutover import angular from 'angular'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; - +import '../../../../webpackShims/ace'; +// required for i18nIdDirective +import 'angular-sanitize'; // type imports import { AppMountContext, ChromeStart, - LegacyCoreStart, + CoreStart, + PluginInitializerContext, SavedObjectsClientContract, ToastsStart, IUiSettingsClient, OverlayStart, } from 'kibana/public'; -import { configureAppAngularModule } from './legacy_imports'; // @ts-ignore import { initGraphApp } from './app'; -import { - Plugin as DataPlugin, - IndexPatternsContract, -} from '../../../../../src/plugins/data/public'; -import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; -import { checkLicense } from '../../../../plugins/graph/common/check_license'; -import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public'; +import { Plugin as DataPlugin, IndexPatternsContract } from '../../../../src/plugins/data/public'; +import { LicensingPluginSetup } from '../../licensing/public'; +import { checkLicense } from '../common/check_license'; +import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; import { createSavedWorkspacesLoader } from './services/persistence/saved_workspace_loader'; -import { Storage } from '../../../../../src/plugins/kibana_utils/public'; +import { Storage } from '../../../../src/plugins/kibana_utils/public'; import { + addAppRedirectMessageToUrl, + configureAppAngularModule, createTopNavDirective, createTopNavHelper, -} from '../../../../../src/plugins/kibana_legacy/public'; -import { addAppRedirectMessageToUrl } from '../../../../../src/plugins/kibana_legacy/public'; +} from '../../../../src/plugins/kibana_legacy/public'; + +import './index.scss'; /** * These are dependencies of the Graph app besides the base dependencies @@ -45,6 +47,8 @@ import { addAppRedirectMessageToUrl } from '../../../../../src/plugins/kibana_le * itself changes */ export interface GraphDependencies { + pluginInitializerContext: PluginInitializerContext; + core: CoreStart; element: HTMLElement; appBasePath: string; capabilities: Record>; @@ -55,7 +59,7 @@ export interface GraphDependencies { config: IUiSettingsClient; toastNotifications: ToastsStart; indexPatterns: IndexPatternsContract; - npData: ReturnType; + data: ReturnType; savedObjectsClient: SavedObjectsClientContract; addBasePath: (url: string) => string; getBasePath: () => string; @@ -67,7 +71,11 @@ export interface GraphDependencies { export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) => { const graphAngularModule = createLocalAngularModule(deps.navigation); - configureAppAngularModule(graphAngularModule, deps.coreStart as LegacyCoreStart, true); + configureAppAngularModule( + graphAngularModule, + { core: deps.core, env: deps.pluginInitializerContext.env }, + true + ); const licenseSubscription = deps.licensing.license$.subscribe(license => { const info = checkLicense(license); @@ -81,7 +89,7 @@ export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) const savedWorkspaceLoader = createSavedWorkspacesLoader({ chrome: deps.coreStart.chrome, - indexPatterns: deps.npData.indexPatterns, + indexPatterns: deps.data.indexPatterns, overlays: deps.coreStart.overlays, savedObjectsClient: deps.coreStart.savedObjects.client, basePath: deps.coreStart.http.basePath, @@ -113,6 +121,7 @@ function mountGraphApp(appBasePath: string, element: HTMLElement) { // make angular-within-angular possible const $injector = angular.bootstrap(mountpoint, [moduleName]); element.appendChild(mountpoint); + element.setAttribute('class', 'kbnLocalApplicationWrapper'); return $injector; } diff --git a/x-pack/legacy/plugins/graph/public/badge.js b/x-pack/plugins/graph/public/badge.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/badge.js rename to x-pack/plugins/graph/public/badge.js diff --git a/x-pack/legacy/plugins/graph/public/components/_app.scss b/x-pack/plugins/graph/public/components/_app.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/_app.scss rename to x-pack/plugins/graph/public/components/_app.scss diff --git a/x-pack/legacy/plugins/graph/public/components/_index.scss b/x-pack/plugins/graph/public/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/_index.scss rename to x-pack/plugins/graph/public/components/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/_search_bar.scss b/x-pack/plugins/graph/public/components/_search_bar.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/_search_bar.scss rename to x-pack/plugins/graph/public/components/_search_bar.scss diff --git a/x-pack/legacy/plugins/graph/public/components/_source_modal.scss b/x-pack/plugins/graph/public/components/_source_modal.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/_source_modal.scss rename to x-pack/plugins/graph/public/components/_source_modal.scss diff --git a/x-pack/legacy/plugins/graph/public/components/app.tsx b/x-pack/plugins/graph/public/components/app.tsx similarity index 96% rename from x-pack/legacy/plugins/graph/public/components/app.tsx rename to x-pack/plugins/graph/public/components/app.tsx index 957a8f66907a13..a57842eaf23f55 100644 --- a/x-pack/legacy/plugins/graph/public/components/app.tsx +++ b/x-pack/plugins/graph/public/components/app.tsx @@ -18,7 +18,7 @@ import { GraphStore } from '../state_management'; import { GuidancePanel } from './guidance_panel'; import { GraphTitle } from './graph_title'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; export interface GraphAppProps extends SearchBarProps { coreStart: CoreStart; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/_field_editor.scss b/x-pack/plugins/graph/public/components/field_manager/_field_editor.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/_field_editor.scss rename to x-pack/plugins/graph/public/components/field_manager/_field_editor.scss diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/_field_picker.scss b/x-pack/plugins/graph/public/components/field_manager/_field_picker.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/_field_picker.scss rename to x-pack/plugins/graph/public/components/field_manager/_field_picker.scss diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/_index.scss b/x-pack/plugins/graph/public/components/field_manager/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/_index.scss rename to x-pack/plugins/graph/public/components/field_manager/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx b/x-pack/plugins/graph/public/components/field_manager/field_editor.tsx similarity index 99% rename from x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx rename to x-pack/plugins/graph/public/components/field_manager/field_editor.tsx index 9c7cffa7757818..78e4180aa2b2ab 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx +++ b/x-pack/plugins/graph/public/components/field_manager/field_editor.tsx @@ -29,7 +29,7 @@ import classNames from 'classnames'; import { WorkspaceField } from '../../types'; import { iconChoices } from '../../helpers/style_choices'; import { LegacyIcon } from '../legacy_icon'; -import { FieldIcon } from '../../../../../../../src/plugins/kibana_react/public'; +import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public'; import { UpdateableFieldProperties } from './field_manager'; import { isEqual } from '../helpers'; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx b/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx rename to x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx b/x-pack/plugins/graph/public/components/field_manager/field_manager.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx rename to x-pack/plugins/graph/public/components/field_manager/field_manager.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx b/x-pack/plugins/graph/public/components/field_manager/field_picker.tsx similarity index 98% rename from x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx rename to x-pack/plugins/graph/public/components/field_manager/field_picker.tsx index 30f1fcffd4f673..f2dc9ba0c6490e 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx +++ b/x-pack/plugins/graph/public/components/field_manager/field_picker.tsx @@ -9,7 +9,7 @@ import { EuiPopover, EuiSelectable, EuiBadge } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; import { WorkspaceField } from '../../types'; -import { FieldIcon } from '../../../../../../../src/plugins/kibana_react/public'; +import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public'; export interface FieldPickerProps { fieldMap: Record; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/index.ts b/x-pack/plugins/graph/public/components/field_manager/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/index.ts rename to x-pack/plugins/graph/public/components/field_manager/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/graph_title.tsx b/x-pack/plugins/graph/public/components/graph_title.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_title.tsx rename to x-pack/plugins/graph/public/components/graph_title.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap b/x-pack/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap rename to x-pack/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/_graph_visualization.scss b/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/_graph_visualization.scss rename to x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/_index.scss b/x-pack/plugins/graph/public/components/graph_visualization/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/_index.scss rename to x-pack/plugins/graph/public/components/graph_visualization/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx b/x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx rename to x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/graph_visualization.tsx b/x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/graph_visualization.tsx rename to x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/index.ts b/x-pack/plugins/graph/public/components/graph_visualization/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/index.ts rename to x-pack/plugins/graph/public/components/graph_visualization/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/_guidance_panel.scss b/x-pack/plugins/graph/public/components/guidance_panel/_guidance_panel.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/guidance_panel/_guidance_panel.scss rename to x-pack/plugins/graph/public/components/guidance_panel/_guidance_panel.scss diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/_index.scss b/x-pack/plugins/graph/public/components/guidance_panel/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/guidance_panel/_index.scss rename to x-pack/plugins/graph/public/components/guidance_panel/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx b/x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx similarity index 98% rename from x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx rename to x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx index d1fcbea2ff5b72..3990abfe87ab36 100644 --- a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx +++ b/x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx @@ -30,7 +30,7 @@ import { import { IndexPatternSavedObject } from '../../types'; import { openSourceModal } from '../../services/source_modal'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; export interface GuidancePanelProps { onFillWorkspace: () => void; diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/index.ts b/x-pack/plugins/graph/public/components/guidance_panel/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/guidance_panel/index.ts rename to x-pack/plugins/graph/public/components/guidance_panel/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/helpers.ts b/x-pack/plugins/graph/public/components/helpers.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/helpers.ts rename to x-pack/plugins/graph/public/components/helpers.ts diff --git a/x-pack/legacy/plugins/graph/public/components/legacy_icon/_index.scss b/x-pack/plugins/graph/public/components/legacy_icon/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/legacy_icon/_index.scss rename to x-pack/plugins/graph/public/components/legacy_icon/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/legacy_icon/_legacy_icon.scss b/x-pack/plugins/graph/public/components/legacy_icon/_legacy_icon.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/legacy_icon/_legacy_icon.scss rename to x-pack/plugins/graph/public/components/legacy_icon/_legacy_icon.scss diff --git a/x-pack/legacy/plugins/graph/public/components/legacy_icon/index.ts b/x-pack/plugins/graph/public/components/legacy_icon/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/legacy_icon/index.ts rename to x-pack/plugins/graph/public/components/legacy_icon/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/legacy_icon/legacy_icon.tsx b/x-pack/plugins/graph/public/components/legacy_icon/legacy_icon.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/legacy_icon/legacy_icon.tsx rename to x-pack/plugins/graph/public/components/legacy_icon/legacy_icon.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/listing.tsx b/x-pack/plugins/graph/public/components/listing.tsx similarity index 98% rename from x-pack/legacy/plugins/graph/public/components/listing.tsx rename to x-pack/plugins/graph/public/components/listing.tsx index 5fa6111b1a2449..aeecc3ab103f36 100644 --- a/x-pack/legacy/plugins/graph/public/components/listing.tsx +++ b/x-pack/plugins/graph/public/components/listing.tsx @@ -10,7 +10,7 @@ import React, { Fragment } from 'react'; import { EuiEmptyPrompt, EuiLink, EuiButton } from '@elastic/eui'; import { CoreStart, ApplicationStart } from 'kibana/public'; -import { TableListView } from '../../../../../../src/plugins/kibana_react/public'; +import { TableListView } from '../../../../../src/plugins/kibana_react/public'; import { GraphWorkspaceSavedObject } from '../types'; export interface ListingProps { diff --git a/x-pack/legacy/plugins/graph/public/components/save_modal.tsx b/x-pack/plugins/graph/public/components/save_modal.tsx similarity index 96% rename from x-pack/legacy/plugins/graph/public/components/save_modal.tsx rename to x-pack/plugins/graph/public/components/save_modal.tsx index a7329c10e93d7c..c4459fb1a794fb 100644 --- a/x-pack/legacy/plugins/graph/public/components/save_modal.tsx +++ b/x-pack/plugins/graph/public/components/save_modal.tsx @@ -7,10 +7,7 @@ import React, { useState } from 'react'; import { EuiFormRow, EuiTextArea, EuiCallOut, EuiSpacer, EuiSwitch } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { - SavedObjectSaveModal, - OnSaveProps, -} from '../../../../../../src/plugins/saved_objects/public'; +import { SavedObjectSaveModal, OnSaveProps } from '../../../../../src/plugins/saved_objects/public'; import { GraphSavePolicy } from '../types/config'; diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx b/x-pack/plugins/graph/public/components/search_bar.test.tsx similarity index 95% rename from x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx rename to x-pack/plugins/graph/public/components/search_bar.test.tsx index 95b7dd22e9fcf4..10778124e2011b 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.test.tsx @@ -9,9 +9,9 @@ import { SearchBar, OuterSearchBarProps } from './search_bar'; import React, { ReactElement } from 'react'; import { CoreStart } from 'src/core/public'; import { act } from 'react-dom/test-utils'; -import { IndexPattern, QueryStringInput } from '../../../../../../src/plugins/data/public'; +import { IndexPattern, QueryStringInput } from '../../../../../src/plugins/data/public'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { I18nProvider } from '@kbn/i18n/react'; import { openSourceModal } from '../services/source_modal'; diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx b/x-pack/plugins/graph/public/components/search_bar.tsx similarity index 98% rename from x-pack/legacy/plugins/graph/public/components/search_bar.tsx rename to x-pack/plugins/graph/public/components/search_bar.tsx index c7c5830cadfe11..ab6d94a78ceecb 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.tsx @@ -18,14 +18,14 @@ import { IndexpatternDatasource, } from '../state_management'; -import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { IndexPattern, QueryStringInput, IDataPluginServices, Query, esKuery, -} from '../../../../../../src/plugins/data/public'; +} from '../../../../../src/plugins/data/public'; export interface OuterSearchBarProps { isLoading: boolean; diff --git a/x-pack/legacy/plugins/graph/public/components/settings/_index.scss b/x-pack/plugins/graph/public/components/settings/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/_index.scss rename to x-pack/plugins/graph/public/components/settings/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/settings/_legacy_icon.scss b/x-pack/plugins/graph/public/components/settings/_legacy_icon.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/_legacy_icon.scss rename to x-pack/plugins/graph/public/components/settings/_legacy_icon.scss diff --git a/x-pack/legacy/plugins/graph/public/components/settings/_url_template_list.scss b/x-pack/plugins/graph/public/components/settings/_url_template_list.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/_url_template_list.scss rename to x-pack/plugins/graph/public/components/settings/_url_template_list.scss diff --git a/x-pack/legacy/plugins/graph/public/components/settings/advanced_settings_form.tsx b/x-pack/plugins/graph/public/components/settings/advanced_settings_form.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/advanced_settings_form.tsx rename to x-pack/plugins/graph/public/components/settings/advanced_settings_form.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/blacklist_form.tsx b/x-pack/plugins/graph/public/components/settings/blacklist_form.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/blacklist_form.tsx rename to x-pack/plugins/graph/public/components/settings/blacklist_form.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/index.ts b/x-pack/plugins/graph/public/components/settings/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/index.ts rename to x-pack/plugins/graph/public/components/settings/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx b/x-pack/plugins/graph/public/components/settings/settings.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx rename to x-pack/plugins/graph/public/components/settings/settings.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/settings.tsx b/x-pack/plugins/graph/public/components/settings/settings.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/settings.tsx rename to x-pack/plugins/graph/public/components/settings/settings.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/url_template_form.tsx b/x-pack/plugins/graph/public/components/settings/url_template_form.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/url_template_form.tsx rename to x-pack/plugins/graph/public/components/settings/url_template_form.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/url_template_list.tsx b/x-pack/plugins/graph/public/components/settings/url_template_list.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/url_template_list.tsx rename to x-pack/plugins/graph/public/components/settings/url_template_list.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/use_list_keys.test.tsx b/x-pack/plugins/graph/public/components/settings/use_list_keys.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/use_list_keys.test.tsx rename to x-pack/plugins/graph/public/components/settings/use_list_keys.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/use_list_keys.ts b/x-pack/plugins/graph/public/components/settings/use_list_keys.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/use_list_keys.ts rename to x-pack/plugins/graph/public/components/settings/use_list_keys.ts diff --git a/x-pack/legacy/plugins/graph/public/components/source_modal.tsx b/x-pack/plugins/graph/public/components/source_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/source_modal.tsx rename to x-pack/plugins/graph/public/components/source_modal.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/source_picker.tsx b/x-pack/plugins/graph/public/components/source_picker.tsx similarity index 94% rename from x-pack/legacy/plugins/graph/public/components/source_picker.tsx rename to x-pack/plugins/graph/public/components/source_picker.tsx index 65a431202fc98d..9172f6ba1c65cd 100644 --- a/x-pack/legacy/plugins/graph/public/components/source_picker.tsx +++ b/x-pack/plugins/graph/public/components/source_picker.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { CoreStart } from 'src/core/public'; -import { SavedObjectFinderUi } from '../../../../../../src/plugins/saved_objects/public'; +import { SavedObjectFinderUi } from '../../../../../src/plugins/saved_objects/public'; import { IndexPatternSavedObject } from '../types'; export interface SourcePickerProps { diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/_index.scss b/x-pack/plugins/graph/public/components/venn_diagram/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/_index.scss rename to x-pack/plugins/graph/public/components/venn_diagram/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/_venn_diagram.scss b/x-pack/plugins/graph/public/components/venn_diagram/_venn_diagram.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/_venn_diagram.scss rename to x-pack/plugins/graph/public/components/venn_diagram/_venn_diagram.scss diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/index.ts b/x-pack/plugins/graph/public/components/venn_diagram/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/index.ts rename to x-pack/plugins/graph/public/components/venn_diagram/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx b/x-pack/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx rename to x-pack/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/venn_diagram.tsx b/x-pack/plugins/graph/public/components/venn_diagram/venn_diagram.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/venn_diagram.tsx rename to x-pack/plugins/graph/public/components/venn_diagram/venn_diagram.tsx diff --git a/x-pack/legacy/plugins/graph/public/helpers/as_observable.ts b/x-pack/plugins/graph/public/helpers/as_observable.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/as_observable.ts rename to x-pack/plugins/graph/public/helpers/as_observable.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/format_http_error.ts b/x-pack/plugins/graph/public/helpers/format_http_error.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/format_http_error.ts rename to x-pack/plugins/graph/public/helpers/format_http_error.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/kql_encoder.test.ts b/x-pack/plugins/graph/public/helpers/kql_encoder.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/kql_encoder.test.ts rename to x-pack/plugins/graph/public/helpers/kql_encoder.test.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/kql_encoder.ts b/x-pack/plugins/graph/public/helpers/kql_encoder.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/kql_encoder.ts rename to x-pack/plugins/graph/public/helpers/kql_encoder.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/outlink_encoders.ts b/x-pack/plugins/graph/public/helpers/outlink_encoders.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/outlink_encoders.ts rename to x-pack/plugins/graph/public/helpers/outlink_encoders.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/style_choices.ts b/x-pack/plugins/graph/public/helpers/style_choices.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/style_choices.ts rename to x-pack/plugins/graph/public/helpers/style_choices.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/url_template.ts b/x-pack/plugins/graph/public/helpers/url_template.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/url_template.ts rename to x-pack/plugins/graph/public/helpers/url_template.ts diff --git a/x-pack/legacy/plugins/graph/public/index.scss b/x-pack/plugins/graph/public/index.scss similarity index 71% rename from x-pack/legacy/plugins/graph/public/index.scss rename to x-pack/plugins/graph/public/index.scss index 067b2c300626df..f4e38de3e93a44 100644 --- a/x-pack/legacy/plugins/graph/public/index.scss +++ b/x-pack/plugins/graph/public/index.scss @@ -1,6 +1,3 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - /* Graph plugin styles */ // Prefix all styles with "gph" to avoid conflicts. diff --git a/x-pack/plugins/graph/public/index.ts b/x-pack/plugins/graph/public/index.ts index 7b2ce676317133..690d2e88dd9c9e 100644 --- a/x-pack/plugins/graph/public/index.ts +++ b/x-pack/plugins/graph/public/index.ts @@ -10,5 +10,3 @@ import { ConfigSchema } from '../config'; export const plugin = (initializerContext: PluginInitializerContext) => new GraphPlugin(initializerContext); - -export { GraphSetup } from './plugin'; diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index e911b400349f8d..5521de705b6ec7 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -6,8 +6,14 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart } from 'kibana/public'; -import { Plugin } from 'src/core/public'; +import { AppMountParameters, Plugin } from 'src/core/public'; import { PluginInitializerContext } from 'kibana/public'; + +import { Storage } from '../../../../src/plugins/kibana_utils/public'; +import { initAngularBootstrap } from '../../../../src/plugins/kibana_legacy/public'; +import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; +import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; + import { toggleNavLink } from './services/toggle_nav_link'; import { LicensingPluginSetup } from '../../licensing/public'; import { checkLicense } from '../common/check_license'; @@ -15,6 +21,7 @@ import { FeatureCatalogueCategory, HomePublicPluginSetup, } from '../../../../src/plugins/home/public'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; import { ConfigSchema } from '../config'; export interface GraphPluginSetupDependencies { @@ -22,12 +29,21 @@ export interface GraphPluginSetupDependencies { home?: HomePublicPluginSetup; } -export class GraphPlugin implements Plugin<{ config: Readonly }, void> { +export interface GraphPluginStartDependencies { + navigation: NavigationStart; + data: DataPublicPluginStart; +} + +export class GraphPlugin + implements Plugin { private licensing: LicensingPluginSetup | null = null; constructor(private initializerContext: PluginInitializerContext) {} - setup(core: CoreSetup, { licensing, home }: GraphPluginSetupDependencies) { + setup( + core: CoreSetup, + { licensing, home }: GraphPluginSetupDependencies + ) { this.licensing = licensing; if (home) { @@ -44,15 +60,42 @@ export class GraphPlugin implements Plugin<{ config: Readonly }, v }); } - return { - /** - * The configuration is temporarily exposed to allow the legacy graph plugin to consume - * the setting. Once the graph plugin is migrated completely, this will become an implementation - * detail. - * @deprecated - */ - config: this.initializerContext.config.get(), - }; + const config = this.initializerContext.config.get(); + + initAngularBootstrap(); + core.application.register({ + id: 'graph', + title: 'Graph', + order: 9000, + appRoute: '/app/graph', + euiIconType: 'graphApp', + category: DEFAULT_APP_CATEGORIES.analyze, + mount: async (params: AppMountParameters) => { + const [coreStart, pluginsStart] = await core.getStartServices(); + const { renderApp } = await import('./application'); + return renderApp({ + ...params, + pluginInitializerContext: this.initializerContext, + licensing, + core: coreStart, + navigation: pluginsStart.navigation, + data: pluginsStart.data, + savedObjectsClient: coreStart.savedObjects.client, + addBasePath: core.http.basePath.prepend, + getBasePath: core.http.basePath.get, + canEditDrillDownUrls: config.canEditDrillDownUrls, + graphSavePolicy: config.savePolicy, + storage: new Storage(window.localStorage), + capabilities: coreStart.application.capabilities.graph, + coreStart, + chrome: coreStart.chrome, + config: coreStart.uiSettings, + toastNotifications: coreStart.notifications.toasts, + indexPatterns: pluginsStart.data!.indexPatterns, + overlays: coreStart.overlays, + }); + }, + }); } start(core: CoreStart) { @@ -66,5 +109,3 @@ export class GraphPlugin implements Plugin<{ config: Readonly }, v stop() {} } - -export type GraphSetup = ReturnType; diff --git a/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts b/x-pack/plugins/graph/public/services/fetch_top_nodes.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts rename to x-pack/plugins/graph/public/services/fetch_top_nodes.test.ts diff --git a/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.ts b/x-pack/plugins/graph/public/services/fetch_top_nodes.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.ts rename to x-pack/plugins/graph/public/services/fetch_top_nodes.ts diff --git a/x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts b/x-pack/plugins/graph/public/services/index_pattern_cache.ts similarity index 90% rename from x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts rename to x-pack/plugins/graph/public/services/index_pattern_cache.ts index 9bbda0b5511933..9cc466b9c20ab5 100644 --- a/x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts +++ b/x-pack/plugins/graph/public/services/index_pattern_cache.ts @@ -5,7 +5,7 @@ */ import { IndexPatternProvider } from '../types'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; export function createCachedIndexPatternProvider( indexPatternGetter: (id: string) => Promise diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts b/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts similarity index 98% rename from x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts rename to x-pack/plugins/graph/public/services/persistence/deserialize.test.ts index efef3d246ac98d..3dda41fcdbdb62 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts +++ b/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts @@ -8,7 +8,7 @@ import { GraphWorkspaceSavedObject, Workspace } from '../../types'; import { savedWorkspaceToAppState } from './deserialize'; import { createWorkspace } from '../../angular/graph_client_workspace'; import { outlinkEncoders } from '../../helpers/outlink_encoders'; -import { IndexPattern } from '../../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../../src/plugins/data/public'; describe('deserialize', () => { let savedWorkspace: GraphWorkspaceSavedObject; diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts b/x-pack/plugins/graph/public/services/persistence/deserialize.ts similarity index 99% rename from x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts rename to x-pack/plugins/graph/public/services/persistence/deserialize.ts index 947e56a6de6eba..06106ed4c4f3fb 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts +++ b/x-pack/plugins/graph/public/services/persistence/deserialize.ts @@ -27,7 +27,7 @@ import { import { IndexPattern, indexPatterns as indexPatternsUtils, -} from '../../../../../../../src/plugins/data/public'; +} from '../../../../../../src/plugins/data/public'; const defaultAdvancedSettings: AdvancedSettings = { useSignificance: true, diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/index.ts b/x-pack/plugins/graph/public/services/persistence/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/index.ts rename to x-pack/plugins/graph/public/services/persistence/index.ts diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts b/x-pack/plugins/graph/public/services/persistence/saved_workspace.ts similarity index 97% rename from x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts rename to x-pack/plugins/graph/public/services/persistence/saved_workspace.ts index 025d5e69359027..e2bd885dc72090 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts +++ b/x-pack/plugins/graph/public/services/persistence/saved_workspace.ts @@ -9,7 +9,7 @@ import { SavedObject, createSavedObjectClass, SavedObjectKibanaServices, -} from '../../../../../../../src/plugins/saved_objects/public'; +} from '../../../../../../src/plugins/saved_objects/public'; export interface SavedWorkspace extends SavedObject { wsState?: string; diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts b/x-pack/plugins/graph/public/services/persistence/saved_workspace_loader.ts similarity index 95% rename from x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts rename to x-pack/plugins/graph/public/services/persistence/saved_workspace_loader.ts index d9bb119006e78a..fb64fbadfbf7cf 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts +++ b/x-pack/plugins/graph/public/services/persistence/saved_workspace_loader.ts @@ -7,7 +7,7 @@ import { IBasePath } from 'kibana/public'; import { i18n } from '@kbn/i18n'; -import { SavedObjectKibanaServices } from '../../../../../../../src/plugins/saved_objects/public'; +import { SavedObjectKibanaServices } from '../../../../../../src/plugins/saved_objects/public'; import { createSavedWorkspaceClass } from './saved_workspace'; export function createSavedWorkspacesLoader( diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_references.test.ts b/x-pack/plugins/graph/public/services/persistence/saved_workspace_references.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_references.test.ts rename to x-pack/plugins/graph/public/services/persistence/saved_workspace_references.test.ts diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_references.ts b/x-pack/plugins/graph/public/services/persistence/saved_workspace_references.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_references.ts rename to x-pack/plugins/graph/public/services/persistence/saved_workspace_references.ts diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts b/x-pack/plugins/graph/public/services/persistence/serialize.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts rename to x-pack/plugins/graph/public/services/persistence/serialize.test.ts diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.ts b/x-pack/plugins/graph/public/services/persistence/serialize.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/serialize.ts rename to x-pack/plugins/graph/public/services/persistence/serialize.ts diff --git a/x-pack/legacy/plugins/graph/public/services/save_modal.tsx b/x-pack/plugins/graph/public/services/save_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/save_modal.tsx rename to x-pack/plugins/graph/public/services/save_modal.tsx diff --git a/x-pack/legacy/plugins/graph/public/services/source_modal.tsx b/x-pack/plugins/graph/public/services/source_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/source_modal.tsx rename to x-pack/plugins/graph/public/services/source_modal.tsx diff --git a/x-pack/legacy/plugins/graph/public/services/url.ts b/x-pack/plugins/graph/public/services/url.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/url.ts rename to x-pack/plugins/graph/public/services/url.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/advanced_settings.ts b/x-pack/plugins/graph/public/state_management/advanced_settings.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/advanced_settings.ts rename to x-pack/plugins/graph/public/state_management/advanced_settings.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/datasource.sagas.ts b/x-pack/plugins/graph/public/state_management/datasource.sagas.ts similarity index 96% rename from x-pack/legacy/plugins/graph/public/state_management/datasource.sagas.ts rename to x-pack/plugins/graph/public/state_management/datasource.sagas.ts index 34d39e71dec555..018b3b42b91576 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/datasource.sagas.ts +++ b/x-pack/plugins/graph/public/state_management/datasource.sagas.ts @@ -17,7 +17,7 @@ import { setDatasource, requestDatasource, } from './datasource'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; /** * Saga loading field information when the datasource is switched. This will overwrite current settings diff --git a/x-pack/legacy/plugins/graph/public/state_management/datasource.test.ts b/x-pack/plugins/graph/public/state_management/datasource.test.ts similarity index 97% rename from x-pack/legacy/plugins/graph/public/state_management/datasource.test.ts rename to x-pack/plugins/graph/public/state_management/datasource.test.ts index 041098a9aaae54..84f3741604e201 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/datasource.test.ts +++ b/x-pack/plugins/graph/public/state_management/datasource.test.ts @@ -10,7 +10,7 @@ import { datasourceSelector, requestDatasource } from './datasource'; import { datasourceSaga } from './datasource.sagas'; import { fieldsSelector } from './fields'; import { updateSettings } from './advanced_settings'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; const waitForPromise = () => new Promise(r => setTimeout(r)); diff --git a/x-pack/legacy/plugins/graph/public/state_management/datasource.ts b/x-pack/plugins/graph/public/state_management/datasource.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/datasource.ts rename to x-pack/plugins/graph/public/state_management/datasource.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/fields.ts b/x-pack/plugins/graph/public/state_management/fields.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/fields.ts rename to x-pack/plugins/graph/public/state_management/fields.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/global.ts b/x-pack/plugins/graph/public/state_management/global.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/global.ts rename to x-pack/plugins/graph/public/state_management/global.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/helpers.ts b/x-pack/plugins/graph/public/state_management/helpers.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/helpers.ts rename to x-pack/plugins/graph/public/state_management/helpers.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/index.ts b/x-pack/plugins/graph/public/state_management/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/index.ts rename to x-pack/plugins/graph/public/state_management/index.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/legacy.test.ts b/x-pack/plugins/graph/public/state_management/legacy.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/legacy.test.ts rename to x-pack/plugins/graph/public/state_management/legacy.test.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/meta_data.test.ts b/x-pack/plugins/graph/public/state_management/meta_data.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/meta_data.test.ts rename to x-pack/plugins/graph/public/state_management/meta_data.test.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/meta_data.ts b/x-pack/plugins/graph/public/state_management/meta_data.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/meta_data.ts rename to x-pack/plugins/graph/public/state_management/meta_data.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/mocks.ts b/x-pack/plugins/graph/public/state_management/mocks.ts similarity index 94% rename from x-pack/legacy/plugins/graph/public/state_management/mocks.ts rename to x-pack/plugins/graph/public/state_management/mocks.ts index 01d6927b9b886a..d06f8a7b3ef0b3 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/mocks.ts +++ b/x-pack/plugins/graph/public/state_management/mocks.ts @@ -10,7 +10,7 @@ import { createStore, applyMiddleware, AnyAction } from 'redux'; import { ChromeStart } from 'kibana/public'; import { GraphStoreDependencies, createRootReducer, GraphStore, GraphState } from './store'; import { Workspace, GraphWorkspaceSavedObject, IndexPatternSavedObject } from '../types'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; jest.mock('ui/new_platform'); @@ -49,7 +49,7 @@ export function createMockGraphStore({ } as unknown) as GraphWorkspaceSavedObject; const mockedDeps: jest.Mocked = { - basePath: 'basepath', + addBasePath: jest.fn((url: string) => url), changeUrl: jest.fn(), chrome: ({ setBreadcrumbs: jest.fn(), @@ -83,7 +83,7 @@ export function createMockGraphStore({ }; const sagaMiddleware = createSagaMiddleware(); - const rootReducer = createRootReducer(mockedDeps.basePath); + const rootReducer = createRootReducer(mockedDeps.addBasePath); const initializedRootReducer = (state: GraphState | undefined, action: AnyAction) => rootReducer(state || (initialStateOverwrites as GraphState), action); diff --git a/x-pack/legacy/plugins/graph/public/state_management/persistence.test.ts b/x-pack/plugins/graph/public/state_management/persistence.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/persistence.test.ts rename to x-pack/plugins/graph/public/state_management/persistence.test.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/persistence.ts b/x-pack/plugins/graph/public/state_management/persistence.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/persistence.ts rename to x-pack/plugins/graph/public/state_management/persistence.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/store.ts b/x-pack/plugins/graph/public/state_management/store.ts similarity index 93% rename from x-pack/legacy/plugins/graph/public/state_management/store.ts rename to x-pack/plugins/graph/public/state_management/store.ts index ecb7335fee5aae..4aeef0338923b8 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/store.ts +++ b/x-pack/plugins/graph/public/state_management/store.ts @@ -46,7 +46,7 @@ export interface GraphState { } export interface GraphStoreDependencies { - basePath: string; + addBasePath: (url: string) => string; indexPatternProvider: IndexPatternProvider; indexPatterns: IndexPatternSavedObject[]; createWorkspace: (index: string, advancedSettings: AdvancedSettings) => void; @@ -65,10 +65,10 @@ export interface GraphStoreDependencies { I18nContext: I18nStart['Context']; } -export function createRootReducer(basePath: string) { +export function createRootReducer(addBasePath: (url: string) => string) { return combineReducers({ fields: fieldsReducer, - urlTemplates: urlTemplatesReducer(basePath), + urlTemplates: urlTemplatesReducer(addBasePath), advancedSettings: advancedSettingsReducer, datasource: datasourceReducer, metaData: metaDataReducer, @@ -91,7 +91,7 @@ function registerSagas(sagaMiddleware: SagaMiddleware, deps: GraphStoreD export const createGraphStore = (deps: GraphStoreDependencies) => { const sagaMiddleware = createSagaMiddleware(); - const rootReducer = createRootReducer(deps.basePath); + const rootReducer = createRootReducer(deps.addBasePath); const store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); diff --git a/x-pack/legacy/plugins/graph/public/state_management/url_templates.test.ts b/x-pack/plugins/graph/public/state_management/url_templates.test.ts similarity index 91% rename from x-pack/legacy/plugins/graph/public/state_management/url_templates.test.ts rename to x-pack/plugins/graph/public/state_management/url_templates.test.ts index c4a3b0fb776a01..c265b2ec277d2b 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/url_templates.test.ts +++ b/x-pack/plugins/graph/public/state_management/url_templates.test.ts @@ -10,9 +10,11 @@ import { outlinkEncoders } from '../helpers/outlink_encoders'; import { UrlTemplate } from '../types'; describe('url_templates', () => { + const addBasePath = (url: string) => url; + describe('reducer', () => { it('should create a default template as soon as datasource is known', () => { - const templates = urlTemplatesReducer('basepath')( + const templates = urlTemplatesReducer(addBasePath)( [], requestDatasource({ type: 'indexpattern', @@ -28,7 +30,7 @@ describe('url_templates', () => { }); it('should keep non-default templates when switching datasource', () => { - const templates = urlTemplatesReducer('basepath')( + const templates = urlTemplatesReducer(addBasePath)( [ { description: 'default template', @@ -52,7 +54,7 @@ describe('url_templates', () => { }); it('should remove isDefault flag when saving a template even if it is spreaded in', () => { - const templates = urlTemplatesReducer('basepath')( + const templates = urlTemplatesReducer(addBasePath)( [ { description: 'abc', diff --git a/x-pack/legacy/plugins/graph/public/state_management/url_templates.ts b/x-pack/plugins/graph/public/state_management/url_templates.ts similarity index 82% rename from x-pack/legacy/plugins/graph/public/state_management/url_templates.ts rename to x-pack/plugins/graph/public/state_management/url_templates.ts index eac29d0ec91168..a0fb9503421a4a 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/url_templates.ts +++ b/x-pack/plugins/graph/public/state_management/url_templates.ts @@ -6,10 +6,10 @@ import actionCreatorFactory from 'typescript-fsa'; import { reducerWithInitialState } from 'typescript-fsa-reducers/dist'; -import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; import { i18n } from '@kbn/i18n'; import rison from 'rison-node'; import { takeEvery, select } from 'redux-saga/effects'; +import { format, parse } from 'url'; import { GraphState, GraphStoreDependencies } from './store'; import { UrlTemplate } from '../types'; import { reset } from './global'; @@ -17,6 +17,7 @@ import { setDatasource, IndexpatternDatasource, requestDatasource } from './data import { outlinkEncoders } from '../helpers/outlink_encoders'; import { urlTemplatePlaceholder } from '../helpers/url_template'; import { matchesOne } from './helpers'; +import { modifyUrl } from '../../../../../src/core/utils'; const actionCreator = actionCreatorFactory('x-pack/graph/urlTemplates'); @@ -32,30 +33,32 @@ const initialTemplates: UrlTemplatesState = []; function generateDefaultTemplate( datasource: IndexpatternDatasource, - basePath: string + addBasePath: (url: string) => string ): UrlTemplate { - const kUrl = new KibanaParsedUrl({ - appId: 'kibana', - basePath, - appPath: '/discover', - }); - - kUrl.addQueryParameter( - '_a', - rison.encode({ + const appPath = modifyUrl('/discover', parsed => { + parsed.query._a = rison.encode({ columns: ['_source'], index: datasource.id, interval: 'auto', query: { language: 'kuery', query: urlTemplatePlaceholder }, sort: ['_score', 'desc'], - }) - ); + }); + }); + const parsedAppPath = parse(`/app/kibana#${appPath}`, true, true); + const formattedAppPath = format({ + protocol: parsedAppPath.protocol, + host: parsedAppPath.host, + pathname: parsedAppPath.pathname, + query: parsedAppPath.query, + hash: parsedAppPath.hash, + }); // replace the URI encoded version of the tag with the unescaped version // so it can be found with String.replace, regexp, etc. - const discoverUrl = kUrl - .getRootRelativePath() - .replace(encodeURIComponent(urlTemplatePlaceholder), urlTemplatePlaceholder); + const discoverUrl = addBasePath(formattedAppPath).replace( + encodeURIComponent(urlTemplatePlaceholder), + urlTemplatePlaceholder + ); return { url: discoverUrl, @@ -68,7 +71,7 @@ function generateDefaultTemplate( }; } -export const urlTemplatesReducer = (basePath: string) => +export const urlTemplatesReducer = (addBasePath: (url: string) => string) => reducerWithInitialState(initialTemplates) .case(reset, () => initialTemplates) .cases([requestDatasource, setDatasource], (templates, datasource) => { @@ -76,7 +79,7 @@ export const urlTemplatesReducer = (basePath: string) => return initialTemplates; } const customTemplates = templates.filter(template => !template.isDefault); - return [...customTemplates, generateDefaultTemplate(datasource, basePath)]; + return [...customTemplates, generateDefaultTemplate(datasource, addBasePath)]; }) .case(loadTemplates, (_currentTemplates, newTemplates) => newTemplates) .case(saveTemplate, (templates, { index: indexToUpdate, template: updatedTemplate }) => { diff --git a/x-pack/legacy/plugins/graph/public/state_management/workspace.ts b/x-pack/plugins/graph/public/state_management/workspace.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/workspace.ts rename to x-pack/plugins/graph/public/state_management/workspace.ts diff --git a/x-pack/legacy/plugins/graph/public/types/app_state.ts b/x-pack/plugins/graph/public/types/app_state.ts similarity index 94% rename from x-pack/legacy/plugins/graph/public/types/app_state.ts rename to x-pack/plugins/graph/public/types/app_state.ts index 876f2cf23b53a9..21e584182785a3 100644 --- a/x-pack/legacy/plugins/graph/public/types/app_state.ts +++ b/x-pack/plugins/graph/public/types/app_state.ts @@ -7,7 +7,7 @@ import { SimpleSavedObject } from 'src/core/public'; import { FontawesomeIcon } from '../helpers/style_choices'; import { OutlinkEncoder } from '../helpers/outlink_encoders'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; export interface UrlTemplate { url: string; diff --git a/x-pack/legacy/plugins/graph/public/types/config.ts b/x-pack/plugins/graph/public/types/config.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/types/config.ts rename to x-pack/plugins/graph/public/types/config.ts diff --git a/x-pack/legacy/plugins/graph/public/types/index.ts b/x-pack/plugins/graph/public/types/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/types/index.ts rename to x-pack/plugins/graph/public/types/index.ts diff --git a/x-pack/legacy/plugins/graph/public/types/persistence.ts b/x-pack/plugins/graph/public/types/persistence.ts similarity index 95% rename from x-pack/legacy/plugins/graph/public/types/persistence.ts rename to x-pack/plugins/graph/public/types/persistence.ts index cdaee5db202d84..b0209153c82e3a 100644 --- a/x-pack/legacy/plugins/graph/public/types/persistence.ts +++ b/x-pack/plugins/graph/public/types/persistence.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObject } from '../../../../../../src/plugins/saved_objects/public'; +import { SavedObject } from '../../../../../src/plugins/saved_objects/public'; import { AdvancedSettings, UrlTemplate, WorkspaceField } from './app_state'; import { WorkspaceNode, WorkspaceEdge } from './workspace_state'; diff --git a/x-pack/legacy/plugins/graph/public/types/workspace_state.ts b/x-pack/plugins/graph/public/types/workspace_state.ts similarity index 97% rename from x-pack/legacy/plugins/graph/public/types/workspace_state.ts rename to x-pack/plugins/graph/public/types/workspace_state.ts index 37a962fd569ced..8c4178eda890f7 100644 --- a/x-pack/legacy/plugins/graph/public/types/workspace_state.ts +++ b/x-pack/plugins/graph/public/types/workspace_state.ts @@ -6,7 +6,7 @@ import { FontawesomeIcon } from '../helpers/style_choices'; import { WorkspaceField, AdvancedSettings } from './app_state'; -import { JsonObject } from '../../../../../../src/plugins/kibana_utils/public'; +import { JsonObject } from '../../../../../src/plugins/kibana_utils/public'; export interface WorkspaceNode { x: number; diff --git a/x-pack/plugins/ml/__mocks__/shared_imports.ts b/x-pack/plugins/ml/__mocks__/shared_imports.ts index d044ab409eb7a5..f5fbbf32d30d7a 100644 --- a/x-pack/plugins/ml/__mocks__/shared_imports.ts +++ b/x-pack/plugins/ml/__mocks__/shared_imports.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export function XJsonMode() {} +export const XJsonMode = jest.fn(); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index f87578c4bce48d..9c239df3571635 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -19,25 +19,36 @@ export type IndexName = string; export type IndexPattern = string; export type DataFrameAnalyticsId = string; +export enum ANALYSIS_CONFIG_TYPE { + OUTLIER_DETECTION = 'outlier_detection', + REGRESSION = 'regression', + CLASSIFICATION = 'classification', +} + interface OutlierAnalysis { + [key: string]: {}; outlier_detection: {}; } -interface RegressionAnalysis { - regression: { - dependent_variable: string; - training_percent?: number; - prediction_field_name?: string; - }; +interface Regression { + dependent_variable: string; + training_percent?: number; + prediction_field_name?: string; +} +export interface RegressionAnalysis { + [key: string]: Regression; + regression: Regression; } -interface ClassificationAnalysis { - classification: { - dependent_variable: string; - training_percent?: number; - num_top_classes?: string; - prediction_field_name?: string; - }; +interface Classification { + dependent_variable: string; + training_percent?: number; + num_top_classes?: string; + prediction_field_name?: string; +} +export interface ClassificationAnalysis { + [key: string]: Classification; + classification: Classification; } export interface LoadExploreDataArg { @@ -136,13 +147,6 @@ type AnalysisConfig = | ClassificationAnalysis | GenericAnalysis; -export enum ANALYSIS_CONFIG_TYPE { - OUTLIER_DETECTION = 'outlier_detection', - REGRESSION = 'regression', - CLASSIFICATION = 'classification', - UNKNOWN = 'unknown', -} - export const getAnalysisType = (analysis: AnalysisConfig) => { const keys = Object.keys(analysis); @@ -150,7 +154,7 @@ export const getAnalysisType = (analysis: AnalysisConfig) => { return keys[0]; } - return ANALYSIS_CONFIG_TYPE.UNKNOWN; + return 'unknown'; }; export const getDependentVar = (analysis: AnalysisConfig) => { @@ -245,6 +249,7 @@ export interface DataFrameAnalyticsConfig { }; source: { index: IndexName | IndexName[]; + query?: any; }; analysis: AnalysisConfig; analyzed_fields: { @@ -254,6 +259,7 @@ export interface DataFrameAnalyticsConfig { model_memory_limit: string; create_time: number; version: string; + allow_lazy_start?: boolean; } export enum REFRESH_ANALYTICS_LIST_STATE { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.test.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.test.ts new file mode 100644 index 00000000000000..6225bca592be39 --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.test.ts @@ -0,0 +1,254 @@ +/* + * 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 { isAdvancedConfig } from './action_clone'; + +describe('Analytics job clone action', () => { + describe('isAdvancedConfig', () => { + test('should detect a classification job created with the form', () => { + const formCreatedClassificationJob = { + description: "Classification job with 'bank-marketing' dataset", + source: { + index: ['bank-marketing'], + query: { + match_all: {}, + }, + }, + dest: { + index: 'dest_bank_1', + results_field: 'ml', + }, + analysis: { + classification: { + dependent_variable: 'y', + num_top_classes: 2, + prediction_field_name: 'y_prediction', + training_percent: 2, + randomize_seed: 6233212276062807000, + }, + }, + analyzed_fields: { + includes: [], + excludes: [], + }, + model_memory_limit: '350mb', + allow_lazy_start: false, + }; + + expect(isAdvancedConfig(formCreatedClassificationJob)).toBe(false); + }); + + test('should detect a outlier_detection job created with the form', () => { + const formCreatedOutlierDetectionJob = { + description: "Outlier detection job with 'glass' dataset", + source: { + index: ['glass_withoutdupl_norm'], + query: { + match_all: {}, + }, + }, + dest: { + index: 'dest_glass_1', + results_field: 'ml', + }, + analysis: { + outlier_detection: { + compute_feature_influence: true, + outlier_fraction: 0.05, + standardization_enabled: true, + }, + }, + analyzed_fields: { + includes: [], + excludes: ['id', 'outlier'], + }, + model_memory_limit: '1mb', + allow_lazy_start: false, + }; + expect(isAdvancedConfig(formCreatedOutlierDetectionJob)).toBe(false); + }); + + test('should detect a regression job created with the form', () => { + const formCreatedRegressionJob = { + description: "Regression job with 'electrical-grid-stability' dataset", + source: { + index: ['electrical-grid-stability'], + query: { + match_all: {}, + }, + }, + dest: { + index: 'dest_grid_1', + results_field: 'ml', + }, + analysis: { + regression: { + dependent_variable: 'stab', + prediction_field_name: 'stab_prediction', + training_percent: 20, + randomize_seed: -2228827740028660200, + }, + }, + analyzed_fields: { + includes: [], + excludes: [], + }, + model_memory_limit: '150mb', + allow_lazy_start: false, + }; + + expect(isAdvancedConfig(formCreatedRegressionJob)).toBe(false); + }); + + test('should detect advanced classification job', () => { + const advancedClassificationJob = { + description: "Classification job with 'bank-marketing' dataset", + source: { + index: ['bank-marketing'], + query: { + match_all: {}, + }, + }, + dest: { + index: 'dest_bank_1', + results_field: 'CUSTOM_RESULT_FIELD', + }, + analysis: { + classification: { + dependent_variable: 'y', + num_top_classes: 2, + prediction_field_name: 'y_prediction', + training_percent: 2, + randomize_seed: 6233212276062807000, + }, + }, + analyzed_fields: { + includes: [], + excludes: [], + }, + model_memory_limit: '350mb', + allow_lazy_start: false, + }; + + expect(isAdvancedConfig(advancedClassificationJob)).toBe(true); + }); + + test('should detect advanced outlier_detection job', () => { + const advancedOutlierDetectionJob = { + description: "Outlier detection job with 'glass' dataset", + source: { + index: ['glass_withoutdupl_norm'], + query: { + // TODO check default for `match` + match_all: {}, + }, + }, + dest: { + index: 'dest_glass_1', + results_field: 'ml', + }, + analysis: { + outlier_detection: { + compute_feature_influence: false, + outlier_fraction: 0.05, + standardization_enabled: true, + }, + }, + analyzed_fields: { + includes: [], + excludes: ['id', 'outlier'], + }, + model_memory_limit: '1mb', + allow_lazy_start: false, + }; + expect(isAdvancedConfig(advancedOutlierDetectionJob)).toBe(true); + }); + + test('should detect a custom query', () => { + const advancedRegressionJob = { + description: "Regression job with 'electrical-grid-stability' dataset", + source: { + index: ['electrical-grid-stability'], + query: { + match: { + custom_field: 'custom_match', + }, + }, + }, + dest: { + index: 'dest_grid_1', + results_field: 'ml', + }, + analysis: { + regression: { + dependent_variable: 'stab', + prediction_field_name: 'stab_prediction', + training_percent: 20, + randomize_seed: -2228827740028660200, + }, + }, + analyzed_fields: { + includes: [], + excludes: [], + }, + model_memory_limit: '150mb', + allow_lazy_start: false, + }; + + expect(isAdvancedConfig(advancedRegressionJob)).toBe(true); + }); + + test('should detect custom analysis settings', () => { + const config = { + description: "Classification clone with 'bank-marketing' dataset", + source: { + index: 'bank-marketing', + }, + dest: { + index: 'bank_classification4', + }, + analyzed_fields: { + excludes: [], + }, + analysis: { + classification: { + dependent_variable: 'y', + training_percent: 71, + max_trees: 1500, + }, + }, + model_memory_limit: '400mb', + }; + + expect(isAdvancedConfig(config)).toBe(true); + }); + + test('should detect as advanced if the prop is unknown', () => { + const config = { + description: "Classification clone with 'bank-marketing' dataset", + source: { + index: 'bank-marketing', + }, + dest: { + index: 'bank_classification4', + }, + analyzed_fields: { + excludes: [], + }, + analysis: { + classification: { + dependent_variable: 'y', + training_percent: 71, + maximum_number_trees: 1500, + }, + }, + model_memory_limit: '400mb', + }; + + expect(isAdvancedConfig(config)).toBe(true); + }); + }); +}); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx new file mode 100644 index 00000000000000..7199453a15d7f9 --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx @@ -0,0 +1,327 @@ +/* + * 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 { EuiButtonEmpty } from '@elastic/eui'; +import React, { FC } from 'react'; +import { isEqual } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { DataFrameAnalyticsConfig, isOutlierAnalysis } from '../../../../common'; +import { isClassificationAnalysis, isRegressionAnalysis } from '../../../../common/analytics'; +import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; +import { State } from '../../hooks/use_create_analytics_form/state'; +import { DataFrameAnalyticsListRow } from './common'; + +interface PropDefinition { + /** + * Indicates if the property is optional + */ + optional: boolean; + /** + * Corresponding property from the form + */ + formKey?: keyof State['form']; + /** + * Default value of the property + */ + defaultValue?: any; + /** + * Indicates if the value has to be ignored + * during detecting advanced configuration + */ + ignore?: boolean; +} + +function isPropDefinition(a: PropDefinition | object): a is PropDefinition { + return a.hasOwnProperty('optional'); +} + +interface AnalyticsJobMetaData { + [key: string]: PropDefinition | AnalyticsJobMetaData; +} + +/** + * Provides a config definition. + */ +const getAnalyticsJobMeta = (config: CloneDataFrameAnalyticsConfig): AnalyticsJobMetaData => ({ + allow_lazy_start: { + optional: true, + defaultValue: false, + }, + description: { + optional: true, + formKey: 'description', + }, + analysis: { + ...(isClassificationAnalysis(config.analysis) + ? { + classification: { + dependent_variable: { + optional: false, + formKey: 'dependentVariable', + }, + training_percent: { + optional: true, + formKey: 'trainingPercent', + }, + eta: { + optional: true, + }, + feature_bag_fraction: { + optional: true, + }, + max_trees: { + optional: true, + }, + gamma: { + optional: true, + }, + lambda: { + optional: true, + }, + num_top_classes: { + optional: true, + defaultValue: 2, + }, + prediction_field_name: { + optional: true, + defaultValue: `${config.analysis.classification.dependent_variable}_prediction`, + }, + randomize_seed: { + optional: true, + // By default it is randomly generated + ignore: true, + }, + num_top_feature_importance_values: { + optional: true, + }, + }, + } + : {}), + ...(isOutlierAnalysis(config.analysis) + ? { + outlier_detection: { + standardization_enabled: { + defaultValue: true, + optional: true, + }, + compute_feature_influence: { + defaultValue: true, + optional: true, + }, + outlier_fraction: { + defaultValue: 0.05, + optional: true, + }, + feature_influence_threshold: { + optional: true, + }, + method: { + optional: true, + }, + n_neighbors: { + optional: true, + }, + }, + } + : {}), + ...(isRegressionAnalysis(config.analysis) + ? { + regression: { + dependent_variable: { + optional: false, + formKey: 'dependentVariable', + }, + training_percent: { + optional: true, + formKey: 'trainingPercent', + }, + eta: { + optional: true, + }, + feature_bag_fraction: { + optional: true, + }, + max_trees: { + optional: true, + }, + gamma: { + optional: true, + }, + lambda: { + optional: true, + }, + prediction_field_name: { + optional: true, + defaultValue: `${config.analysis.regression.dependent_variable}_prediction`, + }, + num_top_feature_importance_values: { + optional: true, + }, + randomize_seed: { + optional: true, + // By default it is randomly generated + ignore: true, + }, + }, + } + : {}), + }, + analyzed_fields: { + excludes: { + optional: true, + formKey: 'excludes', + defaultValue: [], + }, + includes: { + optional: true, + defaultValue: [], + }, + }, + source: { + index: { + formKey: 'sourceIndex', + optional: false, + }, + query: { + optional: true, + defaultValue: { + match_all: {}, + }, + }, + _source: { + optional: true, + }, + }, + dest: { + index: { + optional: false, + formKey: 'destinationIndex', + }, + results_field: { + optional: true, + defaultValue: 'ml', + }, + }, + model_memory_limit: { + optional: true, + formKey: 'modelMemoryLimit', + }, +}); + +/** + * Detects if analytics job configuration were created with + * the advanced editor and not supported by the regular form. + */ +export function isAdvancedConfig(config: any, meta?: AnalyticsJobMetaData): boolean; +export function isAdvancedConfig( + config: CloneDataFrameAnalyticsConfig, + meta: AnalyticsJobMetaData = getAnalyticsJobMeta(config) +): boolean { + for (const configKey in config) { + if (config.hasOwnProperty(configKey)) { + const fieldConfig = config[configKey as keyof typeof config]; + const fieldMeta = meta[configKey as keyof typeof meta]; + + if (!fieldMeta) { + // eslint-disable-next-line no-console + console.info(`Property "${configKey}" is unknown.`); + return true; + } + + if (isPropDefinition(fieldMeta)) { + const isAdvancedSetting = + fieldMeta.formKey === undefined && + fieldMeta.ignore !== true && + !isEqual(fieldMeta.defaultValue, fieldConfig); + + if (isAdvancedSetting) { + // eslint-disable-next-line no-console + console.info( + `Property "${configKey}" is not supported by the form or has a different value to the default.` + ); + return true; + } + } else if (isAdvancedConfig(fieldConfig, fieldMeta)) { + return true; + } + } + } + return false; +} + +export type CloneDataFrameAnalyticsConfig = Omit< + DataFrameAnalyticsConfig, + 'id' | 'version' | 'create_time' +>; + +export function extractCloningConfig( + originalConfig: DataFrameAnalyticsConfig +): CloneDataFrameAnalyticsConfig { + const { + // Omit non-relevant props from the configuration + id, + version, + create_time, + ...cloneConfig + } = originalConfig; + + // Reset the destination index + cloneConfig.dest.index = ''; + return cloneConfig; +} + +export function getCloneAction(createAnalyticsForm: CreateAnalyticsFormProps) { + const buttonText = i18n.translate('xpack.ml.dataframe.analyticsList.cloneJobButtonLabel', { + defaultMessage: 'Clone job', + }); + + const { actions } = createAnalyticsForm; + + const onClick = async (item: DataFrameAnalyticsListRow) => { + await actions.setJobClone(item.config); + }; + + return { + name: buttonText, + description: buttonText, + icon: 'copy', + onClick, + 'data-test-subj': 'mlAnalyticsJobCloneButton', + }; +} + +interface CloneActionProps { + item: DataFrameAnalyticsListRow; + createAnalyticsForm: CreateAnalyticsFormProps; +} + +/** + * Temp component to have Clone job button with the same look as the other actions. + * Replace with {@link getCloneAction} as soon as all the actions are refactored + * to support EuiContext with a valid DOM structure without nested buttons. + */ +export const CloneAction: FC = ({ createAnalyticsForm, item }) => { + const buttonText = i18n.translate('xpack.ml.dataframe.analyticsList.cloneJobButtonLabel', { + defaultMessage: 'Clone job', + }); + const { actions } = createAnalyticsForm; + const onClick = async () => { + await actions.setJobClone(item.config); + }; + + return ( + + {buttonText} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx index 75841b52521bd5..47fc84cf450c04 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx @@ -54,6 +54,7 @@ export const DeleteAction: FC = ({ item }) => { iconType="trash" onClick={openModal} aria-label={buttonDeleteText} + style={{ padding: 0 }} > {buttonDeleteText} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx index eb87bfd96c149f..0436bcfc368470 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx @@ -19,6 +19,8 @@ import { isOutlierAnalysis, isClassificationAnalysis, } from '../../../../common/analytics'; +import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; +import { CloneAction } from './action_clone'; import { getResultsUrl, isDataFrameAnalyticsRunning, DataFrameAnalyticsListRow } from './common'; import { stopAnalytics } from '../../services/analytics_service'; @@ -57,7 +59,7 @@ export const AnalyticsViewAction = { }, }; -export const getActions = () => { +export const getActions = (createAnalyticsForm: CreateAnalyticsFormProps) => { const canStartStopDataFrameAnalytics: boolean = checkPermission('canStartStopDataFrameAnalytics'); return [ @@ -104,5 +106,10 @@ export const getActions = () => { return ; }, }, + { + render: (item: DataFrameAnalyticsListRow) => { + return ; + }, + }, ]; }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 412779513e533d..10be0a74e17e61 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -254,7 +254,8 @@ export const DataFrameAnalyticsList: FC = ({ expandedRowItemIds, setExpandedRowItemIds, isManagementTable, - isMlEnabledInSpace + isMlEnabledInSpace, + createAnalyticsForm ); const sorting = { @@ -375,6 +376,10 @@ export const DataFrameAnalyticsList: FC = ({ })} /> + + {!isManagementTable && createAnalyticsForm?.state.isModalVisible && ( + + )} ); }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx index 07ae2c176c3632..00cd9e3f1e0ddc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx @@ -20,6 +20,7 @@ import { } from '@elastic/eui'; import { getAnalysisType, DataFrameAnalyticsId } from '../../../../common'; +import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; import { getDataFrameAnalyticsProgress, isDataFrameAnalyticsFailed, @@ -125,9 +126,11 @@ export const getColumns = ( expandedRowItemIds: DataFrameAnalyticsId[], setExpandedRowItemIds: React.Dispatch>, isManagementTable: boolean = false, - isMlEnabledInSpace: boolean = true + isMlEnabledInSpace: boolean = true, + createAnalyticsForm?: CreateAnalyticsFormProps ) => { - const actions = isManagementTable === true ? [AnalyticsViewAction] : getActions(); + const actions = + isManagementTable === true ? [AnalyticsViewAction] : getActions(createAnalyticsForm!); function toggleDetails(item: DataFrameAnalyticsListRow) { const index = expandedRowItemIds.indexOf(item.config.id); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx index 399fa4c816877f..7675553515f846 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC, Fragment } from 'react'; +import React, { FC, Fragment, useEffect, useRef } from 'react'; import { EuiCallOut, @@ -41,6 +41,8 @@ export const CreateAnalyticsAdvancedEditor: FC = ({ ac jobIdValid, } = state.form; + const forceInput = useRef(null); + const onChange = (str: string) => { setAdvancedEditorRawString(str); try { @@ -51,6 +53,16 @@ export const CreateAnalyticsAdvancedEditor: FC = ({ ac } }; + // Temp effect to close the context menu popover on Clone button click + useEffect(() => { + if (forceInput.current === null) { + return; + } + const evt = document.createEvent('MouseEvents'); + evt.initEvent('mouseup', true, true); + forceInput.current.dispatchEvent(evt); + }, []); + return ( {requestMessages.map((requestMessage, i) => ( @@ -98,6 +110,11 @@ export const CreateAnalyticsAdvancedEditor: FC = ({ ac ]} > { + if (input) { + forceInput.current = input; + } + }} disabled={isJobCreated} placeholder="analytics job ID" value={jobId} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx index 0958dff7a3f513..e5054e8a6ad2c0 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx @@ -4,18 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, FC } from 'react'; - +import React, { FC } from 'react'; import { EuiButton, EuiToolTip } from '@elastic/eui'; - import { i18n } from '@kbn/i18n'; - import { createPermissionFailureMessage } from '../../../../../privilege/check_privilege'; - import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; -import { CreateAnalyticsFlyoutWrapper } from '../create_analytics_flyout_wrapper'; - export const CreateAnalyticsButton: FC = props => { const { disabled } = props.state; const { openModal } = props.actions; @@ -46,10 +40,5 @@ export const CreateAnalyticsButton: FC = props => { ); } - return ( - - {button} - - - ); + return button; }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_flyout/create_analytics_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_flyout/create_analytics_flyout.tsx index e31c12e2c62d0e..32384e1949d0a3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_flyout/create_analytics_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_flyout/create_analytics_flyout.tsx @@ -26,17 +26,22 @@ export const CreateAnalyticsFlyout: FC = ({ state, }) => { const { closeModal, createAnalyticsJob, startAnalyticsJob } = actions; - const { isJobCreated, isJobStarted, isModalButtonDisabled, isValid } = state; + const { isJobCreated, isJobStarted, isModalButtonDisabled, isValid, cloneJob } = state; + + const headerText = !!cloneJob + ? i18n.translate('xpack.ml.dataframe.analytics.clone.flyoutHeaderTitle', { + defaultMessage: 'Clone job from {job_id}', + values: { job_id: cloneJob.id }, + }) + : i18n.translate('xpack.ml.dataframe.analytics.create.flyoutHeaderTitle', { + defaultMessage: 'Create analytics job', + }); return ( -

- {i18n.translate('xpack.ml.dataframe.analytics.create.flyoutHeaderTitle', { - defaultMessage: 'Create analytics job', - })} -

+

{headerText}

{children} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx index 97484b9da8b686..8e7024d2a9147d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, FC, useEffect, useMemo } from 'react'; +import React, { Fragment, FC, useEffect, useMemo, useRef } from 'react'; import { EuiComboBox, @@ -23,14 +23,13 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { useMlKibana } from '../../../../../contexts/kibana'; import { ml } from '../../../../../services/ml_api_service'; -import { Field } from '../../../../../../../common/types/fields'; import { newJobCapsService } from '../../../../../services/new_job_capabilities_service'; import { useMlContext } from '../../../../../contexts/ml'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; import { - JOB_TYPES, DEFAULT_MODEL_MEMORY_LIMIT, getJobConfigFromFormState, + State, } from '../../hooks/use_create_analytics_form/state'; import { JOB_ID_MAX_LENGTH } from '../../../../../../../common/constants/validation'; import { Messages } from './messages'; @@ -38,7 +37,11 @@ import { JobType } from './job_type'; import { JobDescriptionInput } from './job_description'; import { getModelMemoryLimitErrors } from '../../hooks/use_create_analytics_form/reducer'; import { IndexPattern, indexPatterns } from '../../../../../../../../../../src/plugins/data/public'; -import { DfAnalyticsExplainResponse, FieldSelectionItem } from '../../../../common/analytics'; +import { + ANALYSIS_CONFIG_TYPE, + DfAnalyticsExplainResponse, + FieldSelectionItem, +} from '../../../../common/analytics'; import { shouldAddAsDepVarOption, OMIT_FIELDS } from './form_options_validation'; export const CreateAnalyticsForm: FC = ({ actions, state }) => { @@ -50,6 +53,9 @@ export const CreateAnalyticsForm: FC = ({ actions, sta const mlContext = useMlContext(); const { form, indexPatternsMap, isAdvancedEditorEnabled, isJobCreated, requestMessages } = state; + const forceInput = useRef(null); + const firstUpdate = useRef(true); + const { createIndexPattern, dependentVariable, @@ -91,7 +97,7 @@ export const CreateAnalyticsForm: FC = ({ actions, sta ]); const isJobTypeWithDepVar = - jobType === JOB_TYPES.REGRESSION || jobType === JOB_TYPES.CLASSIFICATION; + jobType === ANALYSIS_CONFIG_TYPE.REGRESSION || jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION; // Find out if index pattern contain numeric fields. Provides a hint in the form // that an analytics jobs is not able to identify outliers if there are no numeric fields present. @@ -139,6 +145,10 @@ export const CreateAnalyticsForm: FC = ({ actions, sta }; const debouncedGetExplainData = debounce(async () => { + const shouldUpdateModelMemoryLimit = !firstUpdate.current || !modelMemoryLimit; + if (firstUpdate.current) { + firstUpdate.current = false; + } // Reset if sourceIndex or jobType changes (jobType requires dependent_variable to be set - // which won't be the case if switching from outlier detection) if (previousSourceIndex !== sourceIndex || previousJobType !== jobType) { @@ -157,7 +167,9 @@ export const CreateAnalyticsForm: FC = ({ actions, sta ); const expectedMemoryWithoutDisk = resp.memory_estimation?.expected_memory_without_disk; - setEstimatedModelMemoryLimit(expectedMemoryWithoutDisk); + if (shouldUpdateModelMemoryLimit) { + setEstimatedModelMemoryLimit(expectedMemoryWithoutDisk); + } // If sourceIndex has changed load analysis field options again if (previousSourceIndex !== sourceIndex || previousJobType !== jobType) { @@ -172,7 +184,7 @@ export const CreateAnalyticsForm: FC = ({ actions, sta } setFormState({ - ...(!modelMemoryLimit ? { modelMemoryLimit: expectedMemoryWithoutDisk } : {}), + ...(shouldUpdateModelMemoryLimit ? { modelMemoryLimit: expectedMemoryWithoutDisk } : {}), excludesOptions: analyzedFieldsOptions, loadingFieldOptions: false, fieldOptionsFetchFail: false, @@ -180,13 +192,13 @@ export const CreateAnalyticsForm: FC = ({ actions, sta }); } else { setFormState({ - ...(!modelMemoryLimit ? { modelMemoryLimit: expectedMemoryWithoutDisk } : {}), + ...(shouldUpdateModelMemoryLimit ? { modelMemoryLimit: expectedMemoryWithoutDisk } : {}), }); } } catch (e) { let errorMessage; if ( - jobType === JOB_TYPES.CLASSIFICATION && + jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION && e.message !== undefined && e.message.includes('status_exception') && e.message.includes('must have at most') @@ -202,16 +214,15 @@ export const CreateAnalyticsForm: FC = ({ actions, sta fieldOptionsFetchFail: true, maxDistinctValuesError: errorMessage, loadingFieldOptions: false, - modelMemoryLimit: fallbackModelMemoryLimit, + ...(shouldUpdateModelMemoryLimit ? { modelMemoryLimit: fallbackModelMemoryLimit } : {}), }); } }, 400); - const loadDepVarOptions = async () => { + const loadDepVarOptions = async (formState: State['form']) => { setFormState({ loadingDepVarOptions: true, // clear when the source index changes - dependentVariable: '', maxDistinctValuesError: undefined, sourceIndexFieldsCheckFailed: false, sourceIndexContainsNumericalFields: true, @@ -222,23 +233,39 @@ export const CreateAnalyticsForm: FC = ({ actions, sta ); if (indexPattern !== undefined) { + const formStateUpdate: { + loadingDepVarOptions: boolean; + dependentVariableFetchFail: boolean; + dependentVariableOptions: State['form']['dependentVariableOptions']; + dependentVariable?: State['form']['dependentVariable']; + } = { + loadingDepVarOptions: false, + dependentVariableFetchFail: false, + dependentVariableOptions: [] as State['form']['dependentVariableOptions'], + }; + await newJobCapsService.initializeFromIndexPattern(indexPattern); // Get fields and filter for supported types for job type const { fields } = newJobCapsService; - const depVarOptions: EuiComboBoxOptionOption[] = []; - - fields.forEach((field: Field) => { + let resetDependentVariable = true; + for (const field of fields) { if (shouldAddAsDepVarOption(field, jobType)) { - depVarOptions.push({ label: field.id }); + formStateUpdate.dependentVariableOptions.push({ + label: field.id, + }); + + if (formState.dependentVariable === field.id) { + resetDependentVariable = false; + } } - }); + } - setFormState({ - dependentVariableOptions: depVarOptions, - loadingDepVarOptions: false, - dependentVariableFetchFail: false, - }); + if (resetDependentVariable) { + formStateUpdate.dependentVariable = ''; + } + + setFormState(formStateUpdate); } } catch (e) { setFormState({ loadingDepVarOptions: false, dependentVariableFetchFail: true }); @@ -284,10 +311,10 @@ export const CreateAnalyticsForm: FC = ({ actions, sta useEffect(() => { if (isJobTypeWithDepVar && sourceIndexNameEmpty === false) { - loadDepVarOptions(); + loadDepVarOptions(form); } - if (jobType === JOB_TYPES.OUTLIER_DETECTION && sourceIndexNameEmpty === false) { + if (jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION && sourceIndexNameEmpty === false) { validateSourceIndexFields(); } }, [sourceIndex, jobType, sourceIndexNameEmpty]); @@ -297,7 +324,8 @@ export const CreateAnalyticsForm: FC = ({ actions, sta jobType !== undefined && sourceIndex !== '' && sourceIndexNameValid === true; const hasRequiredAnalysisFields = - (isJobTypeWithDepVar && dependentVariable !== '') || jobType === JOB_TYPES.OUTLIER_DETECTION; + (isJobTypeWithDepVar && dependentVariable !== '') || + jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION; if (hasBasicRequiredFields && hasRequiredAnalysisFields) { debouncedGetExplainData(); @@ -308,6 +336,16 @@ export const CreateAnalyticsForm: FC = ({ actions, sta }; }, [jobType, sourceIndex, sourceIndexNameEmpty, dependentVariable, trainingPercent]); + // Temp effect to close the context menu popover on Clone button click + useEffect(() => { + if (forceInput.current === null) { + return; + } + const evt = document.createEvent('MouseEvents'); + evt.initEvent('mouseup', true, true); + forceInput.current.dispatchEvent(evt); + }, []); + return ( @@ -375,6 +413,11 @@ export const CreateAnalyticsForm: FC = ({ actions, sta ]} > { + if (input) { + forceInput.current = input; + } + }} disabled={isJobCreated} placeholder={i18n.translate('xpack.ml.dataframe.analytics.create.jobIdPlaceholder', { defaultMessage: 'Job ID', @@ -495,7 +538,8 @@ export const CreateAnalyticsForm: FC = ({ actions, sta data-test-subj="mlAnalyticsCreateJobFlyoutDestinationIndexInput" /> - {(jobType === JOB_TYPES.REGRESSION || jobType === JOB_TYPES.CLASSIFICATION) && ( + {(jobType === ANALYSIS_CONFIG_TYPE.REGRESSION || + jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION) && ( = ({ description, setFormState }) => label={i18n.translate('xpack.ml.dataframe.analytics.create.jobDescription.label', { defaultMessage: 'Job description', })} - helpText={helpText} > { const value = e.target.value; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/job_type.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/job_type.tsx index ffed1ebf522f40..0269ae2915d573 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/job_type.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/job_type.tsx @@ -8,8 +8,9 @@ import React, { Fragment, FC } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSelect } from '@elastic/eui'; +import { ANALYSIS_CONFIG_TYPE } from '../../../../common'; -import { AnalyticsJobType, JOB_TYPES } from '../../hooks/use_create_analytics_form/state'; +import { AnalyticsJobType } from '../../hooks/use_create_analytics_form/state'; interface Props { type: AnalyticsJobType; @@ -42,9 +43,9 @@ export const JobType: FC = ({ type, setFormState }) => { ); const helpText = { - outlier_detection: outlierHelpText, - regression: regressionHelpText, - classification: classificationHelpText, + [ANALYSIS_CONFIG_TYPE.REGRESSION]: regressionHelpText, + [ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION]: outlierHelpText, + [ANALYSIS_CONFIG_TYPE.CLASSIFICATION]: classificationHelpText, }; return ( @@ -56,7 +57,7 @@ export const JobType: FC = ({ type, setFormState }) => { helpText={type !== undefined ? helpText[type] : ''} > ({ + options={Object.values(ANALYSIS_CONFIG_TYPE).map(jobType => ({ value: jobType, text: jobType.replace(/_/g, ' '), }))} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts index 70228f0238fda0..8cedc38b1b59b2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { DataFrameAnalyticsConfig } from '../../../../common'; import { FormMessage, State, SourceIndexMap } from './state'; export enum ACTION { @@ -25,6 +26,7 @@ export enum ACTION { SET_JOB_IDS, SWITCH_TO_ADVANCED_EDITOR, SET_ESTIMATED_MODEL_MEMORY_LIMIT, + SET_JOB_CLONE, } export type Action = @@ -61,13 +63,14 @@ export type Action = | { type: ACTION.SET_IS_MODAL_VISIBLE; isModalVisible: State['isModalVisible'] } | { type: ACTION.SET_JOB_CONFIG; payload: State['jobConfig'] } | { type: ACTION.SET_JOB_IDS; jobIds: State['jobIds'] } - | { type: ACTION.SET_ESTIMATED_MODEL_MEMORY_LIMIT; value: State['estimatedModelMemoryLimit'] }; + | { type: ACTION.SET_ESTIMATED_MODEL_MEMORY_LIMIT; value: State['estimatedModelMemoryLimit'] } + | { type: ACTION.SET_JOB_CLONE; cloneJob: DataFrameAnalyticsConfig }; // Actions wrapping the dispatcher exposed by the custom hook export interface ActionDispatchers { closeModal: () => void; createAnalyticsJob: () => void; - openModal: () => void; + openModal: () => Promise; resetAdvancedEditorMessages: () => void; setAdvancedEditorRawString: (payload: State['advancedEditorRawString']) => void; setFormState: (payload: Partial) => void; @@ -76,4 +79,5 @@ export interface ActionDispatchers { startAnalyticsJob: () => void; switchToAdvancedEditor: () => void; setEstimatedModelMemoryLimit: (value: State['estimatedModelMemoryLimit']) => void; + setJobClone: (cloneJob: DataFrameAnalyticsConfig) => Promise; } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts index 5c989f7248a9eb..8112a0fdb9e29c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts @@ -6,11 +6,11 @@ import { merge } from 'lodash'; -import { DataFrameAnalyticsConfig } from '../../../../common'; +import { ANALYSIS_CONFIG_TYPE, DataFrameAnalyticsConfig } from '../../../../common'; import { ACTION } from './actions'; import { reducer, validateAdvancedEditor, validateMinMML } from './reducer'; -import { getInitialState, JOB_TYPES } from './state'; +import { getInitialState } from './state'; type SourceIndex = DataFrameAnalyticsConfig['source']['index']; @@ -52,7 +52,7 @@ describe('useCreateAnalyticsForm', () => { destinationIndex: 'the-destination-index', jobId: 'the-analytics-job-id', sourceIndex: 'the-source-index', - jobType: JOB_TYPES.OUTLIER_DETECTION, + jobType: ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION, modelMemoryLimit: '200mb', }, }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts index 5f21f17b92735f..d045749a1a0dd5 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts @@ -8,10 +8,11 @@ import { i18n } from '@kbn/i18n'; import { memoize } from 'lodash'; // @ts-ignore import numeral from '@elastic/numeral'; +import { isEmpty } from 'lodash'; import { isValidIndexName } from '../../../../../../../common/util/es_utils'; import { Action, ACTION } from './actions'; -import { getInitialState, getJobConfigFromFormState, State, JOB_TYPES } from './state'; +import { getInitialState, getJobConfigFromFormState, State } from './state'; import { isJobIdValid, validateModelMemoryLimitUnits, @@ -30,6 +31,7 @@ import { getDependentVar, isRegressionAnalysis, isClassificationAnalysis, + ANALYSIS_CONFIG_TYPE, } from '../../../../common/analytics'; import { indexPatterns } from '../../../../../../../../../../src/plugins/data/public'; @@ -142,7 +144,7 @@ export const validateAdvancedEditor = (state: State): State => { if ( jobConfig.analysis === undefined && - (jobType === JOB_TYPES.CLASSIFICATION || jobType === JOB_TYPES.REGRESSION) + (jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION || jobType === ANALYSIS_CONFIG_TYPE.REGRESSION) ) { dependentVariableEmpty = true; } @@ -315,7 +317,8 @@ const validateForm = (state: State): State => { const jobTypeEmpty = jobType === undefined; const dependentVariableEmpty = - (jobType === JOB_TYPES.REGRESSION || jobType === JOB_TYPES.CLASSIFICATION) && + (jobType === ANALYSIS_CONFIG_TYPE.REGRESSION || + jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION) && dependentVariable === ''; const mmlValidationResult = validateMml(estimatedModelMemoryLimit, modelMemoryLimit); @@ -437,7 +440,11 @@ export function reducer(state: State, action: Action): State { } case ACTION.SWITCH_TO_ADVANCED_EDITOR: - const jobConfig = getJobConfigFromFormState(state.form); + let { jobConfig } = state; + const isJobConfigEmpty = isEmpty(state.jobConfig); + if (isJobConfigEmpty) { + jobConfig = getJobConfigFromFormState(state.form); + } return validateAdvancedEditor({ ...state, advancedEditorRawString: JSON.stringify(jobConfig, null, 2), @@ -450,6 +457,12 @@ export function reducer(state: State, action: Action): State { ...state, estimatedModelMemoryLimit: action.value, }; + + case ACTION.SET_JOB_CLONE: + return { + ...state, + cloneJob: action.cloneJob, + }; } return state; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts index 170700d35e6511..515e0e42bd873a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts @@ -7,9 +7,16 @@ import { EuiComboBoxOptionOption } from '@elastic/eui'; import { DeepPartial } from '../../../../../../../common/types/common'; import { checkPermission } from '../../../../../privilege/check_privilege'; -import { mlNodesAvailable } from '../../../../../ml_nodes_check/check_ml_nodes'; +import { mlNodesAvailable } from '../../../../../ml_nodes_check'; -import { DataFrameAnalyticsId, DataFrameAnalyticsConfig } from '../../../../common'; +import { + isClassificationAnalysis, + isRegressionAnalysis, + DataFrameAnalyticsId, + DataFrameAnalyticsConfig, + ANALYSIS_CONFIG_TYPE, +} from '../../../../common/analytics'; +import { CloneDataFrameAnalyticsConfig } from '../../components/analytics_list/action_clone'; export enum DEFAULT_MODEL_MEMORY_LIMIT { regression = '100mb', @@ -21,7 +28,7 @@ export enum DEFAULT_MODEL_MEMORY_LIMIT { export type EsIndexName = string; export type DependentVariable = string; export type IndexPatternTitle = string; -export type AnalyticsJobType = JOB_TYPES | undefined; +export type AnalyticsJobType = ANALYSIS_CONFIG_TYPE | undefined; type IndexPatternId = string; export type SourceIndexMap = Record< IndexPatternTitle, @@ -33,12 +40,6 @@ export interface FormMessage { message: string; } -export enum JOB_TYPES { - OUTLIER_DETECTION = 'outlier_detection', - REGRESSION = 'regression', - CLASSIFICATION = 'classification', -} - export interface State { advancedEditorMessages: FormMessage[]; advancedEditorRawString: string; @@ -90,6 +91,7 @@ export interface State { jobIds: DataFrameAnalyticsId[]; requestMessages: FormMessage[]; estimatedModelMemoryLimit: string; + cloneJob?: DataFrameAnalyticsConfig; } export const getInitialState = (): State => ({ @@ -174,8 +176,8 @@ export const getJobConfigFromFormState = ( }; if ( - formState.jobType === JOB_TYPES.REGRESSION || - formState.jobType === JOB_TYPES.CLASSIFICATION + formState.jobType === ANALYSIS_CONFIG_TYPE.REGRESSION || + formState.jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION ) { jobConfig.analysis = { [formState.jobType]: { @@ -187,3 +189,35 @@ export const getJobConfigFromFormState = ( return jobConfig; }; + +/** + * Extracts form state for a job clone from the analytics job configuration. + * For cloning we keep job id and destination index empty. + */ +export function getCloneFormStateFromJobConfig( + analyticsJobConfig: CloneDataFrameAnalyticsConfig +): Partial { + const jobType = Object.keys(analyticsJobConfig.analysis)[0] as ANALYSIS_CONFIG_TYPE; + + const resultState: Partial = { + jobType, + description: analyticsJobConfig.description ?? '', + sourceIndex: Array.isArray(analyticsJobConfig.source.index) + ? analyticsJobConfig.source.index.join(',') + : analyticsJobConfig.source.index, + modelMemoryLimit: analyticsJobConfig.model_memory_limit, + excludes: analyticsJobConfig.analyzed_fields.excludes, + }; + + if ( + isRegressionAnalysis(analyticsJobConfig.analysis) || + isClassificationAnalysis(analyticsJobConfig.analysis) + ) { + const analysisConfig = analyticsJobConfig.analysis[jobType]; + + resultState.dependentVariable = analysisConfig.dependent_variable; + resultState.trainingPercent = analysisConfig.training_percent; + } + + return resultState; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index 9a243e1b0316d8..74161d7c48c246 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -17,6 +17,10 @@ import { DataFrameAnalyticsId, DataFrameAnalyticsConfig, } from '../../../../common'; +import { + extractCloningConfig, + isAdvancedConfig, +} from '../../components/analytics_list/action_clone'; import { ActionDispatchers, ACTION } from './actions'; import { reducer } from './reducer'; @@ -27,6 +31,7 @@ import { FormMessage, State, SourceIndexMap, + getCloneFormStateFromJobConfig, } from './state'; export interface CreateAnalyticsFormProps { @@ -187,9 +192,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { } }; - const openModal = async () => { - resetForm(); - + const prepareFormValidation = async () => { // re-fetch existing analytics job IDs and indices for form validation try { setJobIds( @@ -248,7 +251,11 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { ), }); } + }; + const openModal = async () => { + resetForm(); + await prepareFormValidation(); dispatch({ type: ACTION.OPEN_MODAL }); }; @@ -301,6 +308,23 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { dispatch({ type: ACTION.SET_ESTIMATED_MODEL_MEMORY_LIMIT, value }); }; + const setJobClone = async (cloneJob: DataFrameAnalyticsConfig) => { + resetForm(); + await prepareFormValidation(); + + const config = extractCloningConfig(cloneJob); + if (isAdvancedConfig(config)) { + setJobConfig(config); + switchToAdvancedEditor(); + } else { + setFormState(getCloneFormStateFromJobConfig(config)); + setEstimatedModelMemoryLimit(config.model_memory_limit); + } + + dispatch({ type: ACTION.SET_JOB_CLONE, cloneJob }); + dispatch({ type: ACTION.OPEN_MODAL }); + }; + const actions: ActionDispatchers = { closeModal, createAnalyticsJob, @@ -313,6 +337,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { startAnalyticsJob, switchToAdvancedEditor, setEstimatedModelMemoryLimit, + setJobClone, }; return { state, actions }; diff --git a/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts b/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts index 6ac42f77172599..1560b78b3c0501 100644 --- a/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts +++ b/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts @@ -10,9 +10,8 @@ import { UMResolver } from '../../../../../legacy/plugins/uptime/common/graphql/ import { GetMonitorStatesQueryArgs, MonitorSummaryResult, - StatesIndexStatus, } from '../../../../../legacy/plugins/uptime/common/graphql/types'; -import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants/context_defaults'; +import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants'; export type UMGetMonitorStatesResolver = UMResolver< MonitorSummaryResult | Promise, @@ -21,19 +20,11 @@ export type UMGetMonitorStatesResolver = UMResolver< UMContext >; -export type UMStatesIndexExistsResolver = UMResolver< - StatesIndexStatus | Promise, - any, - {}, - UMContext ->; - export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( libs: UMServerLibs ): { Query: { getMonitorStates: UMGetMonitorStatesResolver; - getStatesIndexStatus: UMStatesIndexExistsResolver; }; } => { return { @@ -64,7 +55,7 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( }), ]); - const totalSummaryCount = indexStatus?.docCount ?? { count: undefined }; + const totalSummaryCount = indexStatus?.docCount ?? 0; return { summaries, @@ -73,9 +64,6 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( totalSummaryCount, }; }, - async getStatesIndexStatus(_resolver, {}, { APICaller }): Promise { - return await libs.requests.getIndexStatus({ callES: APICaller }); - }, }, }; }; diff --git a/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts b/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts index 198f97eab96524..d088aed951204e 100644 --- a/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts +++ b/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts @@ -156,15 +156,7 @@ export const monitorStatesSchema = gql` "The objects representing the state of a series of heartbeat monitors." summaries: [MonitorSummary!] "The number of summaries." - totalSummaryCount: DocCount! - } - - "Represents the current status of the uptime index." - type StatesIndexStatus { - "Flag denoting whether the index exists." - indexExists: Boolean! - "The number of documents in the index." - docCount: DocCount + totalSummaryCount: Int! } enum CursorDirection { @@ -186,8 +178,5 @@ export const monitorStatesSchema = gql` filters: String statusFilter: String ): MonitorSummaryResult - - "Fetches details about the uptime index." - getStatesIndexStatus: StatesIndexStatus! } `; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts index 95aa7eeef88e19..d8a05c08b14173 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts @@ -5,8 +5,8 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { StatesIndexStatus } from '../../../../../legacy/plugins/uptime/common/graphql/types'; import { INDEX_NAMES } from '../../../../../legacy/plugins/uptime/common/constants'; +import { StatesIndexStatus } from '../../../../../legacy/plugins/uptime/common/runtime_types'; export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = async ({ callES }) => { const { @@ -15,8 +15,6 @@ export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = asy } = await callES('count', { index: INDEX_NAMES.HEARTBEAT }); return { indexExists: total > 0, - docCount: { - count, - }, + docCount: count, }; }; diff --git a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts index 6fd77afe711d4d..7f192994bd075a 100644 --- a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts +++ b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts @@ -5,11 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { - Ping, - PingResults, - StatesIndexStatus, -} from '../../../../../legacy/plugins/uptime/common/graphql/types'; +import { Ping, PingResults } from '../../../../../legacy/plugins/uptime/common/graphql/types'; import { GetFilterBarParams, GetLatestMonitorParams, @@ -26,6 +22,7 @@ import { MonitorDetails, MonitorLocations, Snapshot, + StatesIndexStatus, } from '../../../../../legacy/plugins/uptime/common/runtime_types'; import { GetMonitorStatesResult } from './get_monitor_states'; import { GetSnapshotCountParams } from './get_snapshot_counts'; diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index 69981b7860d596..b0cc38ebfb4b6b 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -6,7 +6,6 @@ import { createGetOverviewFilters } from './overview_filters'; import { createGetPingsRoute } from './pings'; -import { createGetIndexPatternRoute } from './index_pattern'; import { createLogMonitorPageRoute, createLogOverviewPageRoute } from './telemetry'; import { createGetSnapshotCount } from './snapshot'; import { UMRestApiRouteFactory } from './types'; @@ -18,6 +17,7 @@ import { } from './monitors'; import { createGetPingHistogramRoute } from './pings/get_ping_histogram'; import { createGetMonitorDurationRoute } from './monitors/monitors_durations'; +import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state'; export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; @@ -27,6 +27,7 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createGetOverviewFilters, createGetPingsRoute, createGetIndexPatternRoute, + createGetIndexStatusRoute, createGetMonitorRoute, createGetMonitorDetailsRoute, createGetMonitorLocationsRoute, diff --git a/x-pack/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts similarity index 100% rename from x-pack/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts rename to x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts new file mode 100644 index 00000000000000..44799aa19c140e --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts @@ -0,0 +1,29 @@ +/* + * 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 { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; +import { REST_API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; + +export const createGetIndexStatusRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: REST_API_URLS.INDEX_STATUS, + validate: false, + options: { + tags: ['access:uptime'], + }, + handler: async ({ callES }, _context, _request, response): Promise => { + try { + return response.ok({ + body: { + ...(await libs.requests.getIndexStatus({ callES })), + }, + }); + } catch (e) { + return response.internalError({ body: { message: e.message } }); + } + }, +}); diff --git a/x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts b/x-pack/plugins/uptime/server/rest_api/index_state/index.ts similarity index 82% rename from x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts rename to x-pack/plugins/uptime/server/rest_api/index_state/index.ts index b35e2e7b65a294..ff44794bfe7d1e 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/index.ts @@ -5,3 +5,4 @@ */ export { createGetIndexPatternRoute } from './get_index_pattern'; +export { createGetIndexStatusRoute } from './get_index_status'; diff --git a/x-pack/test/api_integration/apis/uptime/feature_controls.ts b/x-pack/test/api_integration/apis/uptime/feature_controls.ts index adbfacb014e9f9..15666acab2335d 100644 --- a/x-pack/test/api_integration/apis/uptime/feature_controls.ts +++ b/x-pack/test/api_integration/apis/uptime/feature_controls.ts @@ -5,9 +5,9 @@ */ import expect from '@kbn/expect'; -import { docCountQueryString } from '../../../../legacy/plugins/uptime/public/queries'; import { FtrProviderContext } from '../../ftr_provider_context'; import { PINGS_DATE_RANGE_END, PINGS_DATE_RANGE_START } from './constants'; +import { REST_API_URLS } from '../../../../legacy/plugins/uptime/common/constants'; export default function featureControlsTests({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); @@ -26,22 +26,13 @@ export default function featureControlsTests({ getService }: FtrProviderContext) expect(result.response).to.have.property('statusCode', 200); }; - const executeGraphQLQuery = async (username: string, password: string, spaceId?: string) => { + const executeRESTAPIQuery = async (username: string, password: string, spaceId?: string) => { const basePath = spaceId ? `/s/${spaceId}` : ''; - const getDocCountQuery = { - operationName: null, - query: docCountQueryString, - variables: { - dateRangeStart: '2019-01-28T17:40:08.078Z', - dateRangeEnd: '2019-01-28T19:00:16.078Z', - }, - }; return await supertest - .post(`${basePath}/api/uptime/graphql`) + .get(basePath + REST_API_URLS.INDEX_STATUS) .auth(username, password) .set('kbn-xsrf', 'foo') - .send({ ...getDocCountQuery }) .then((response: any) => ({ error: undefined, response })) .catch((error: any) => ({ error, response: undefined })); }; @@ -82,7 +73,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -121,7 +112,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expectResponse(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -163,7 +154,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -232,7 +223,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }); it('user_1 can access APIs in space_1', async () => { - const graphQLResult = await executeGraphQLQuery(username, password, space1Id); + const graphQLResult = await executeRESTAPIQuery(username, password, space1Id); expectResponse(graphQLResult); const pingsResult = await executePingsRequest(username, password, space1Id); @@ -240,7 +231,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }); it(`user_1 can't access APIs in space_2`, async () => { - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); diff --git a/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js b/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js deleted file mode 100644 index 1aa69faaab736e..00000000000000 --- a/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 { docCountQueryString } from '../../../../../legacy/plugins/uptime/public/queries'; -import { expectFixtureEql } from './helpers/expect_fixture_eql'; - -export default function({ getService }) { - describe('docCount query', () => { - before('load heartbeat data', () => getService('esArchiver').load('uptime/full_heartbeat')); - after('unload heartbeat index', () => getService('esArchiver').unload('uptime/full_heartbeat')); - - const supertest = getService('supertest'); - - it(`will fetch the index's count`, async () => { - const getDocCountQuery = { - operationName: null, - query: docCountQueryString, - variables: { - dateRangeStart: '2019-01-28T17:40:08.078Z', - dateRangeEnd: '2025-01-28T19:00:16.078Z', - }, - }; - const { - body: { data }, - } = await supertest - .post('/api/uptime/graphql') - .set('kbn-xsrf', 'foo') - .send({ ...getDocCountQuery }); - - expectFixtureEql(data, 'doc_count'); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json deleted file mode 100644 index 4daf223a79a697..00000000000000 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "statesIndexStatus": { - "docCount": { - "count": 2000 - }, - "indexExists": true - } -} \ No newline at end of file diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json index 59f5f95e7d8400..05724f0716e8d3 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0009-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json index 9a1363f00578a6..6e62787069f40d 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": null, - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0002-up", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -198,4 +194,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json index 59f5f95e7d8400..05724f0716e8d3 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0009-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json index 5c07b4daaf543b..6cbe4ee3659a86 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0090-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": null, - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0090-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json index 71184093f4318a..9a3f781735cb72 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0080-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0089-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0080-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json index 3b15ea3c24eeb4..4f4af9c2c60129 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0010-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0019-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0010-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json index eb6512d2f75b37..fe48ad49d13ba7 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0009-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json index aee4fa6946fc0d..70ca665704a791 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0020-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0029-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0020-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json index 03164f794a4d5d..3f09c951ec2fa4 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0010-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0019-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0010-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json index 488fdab14f1e2a..cdc0f32c9765ec 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0030-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0039-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0030-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json index 79ce05d86f533a..9f6d004380c162 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0020-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0029-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0020-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json index ef62cdd86c2a9e..dedddb2a78ade8 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0040-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0049-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0040-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json index 34e8269cb95d91..fabcf704049520 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0030-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0039-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0030-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json index d733467c3f9b60..943cc68249dc13 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0050-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0059-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0050-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json index 12e27106bd533d..564f58f59f373d 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0040-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0049-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0040-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json index d0f2b820f8327c..cb94273e91fd84 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0060-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0069-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0060-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json index cf0c8641cc87b3..7aac62bba84f78 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0050-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0059-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0050-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json index 2801e94e034c73..08cbd0d878b440 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0070-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0079-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0070-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json index 7fcbd4dd596590..8de639b705ee9a 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0060-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0069-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0060-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json index 0adb7ad0b0dba4..c38f5c801a267a 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0080-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0089-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0080-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json index a796be38bd0d9b..5c2ec8512e320a 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0070-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0079-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0070-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/index.js b/x-pack/test/api_integration/apis/uptime/graphql/index.js index c2fdc57edede37..ee22974d471701 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/index.js +++ b/x-pack/test/api_integration/apis/uptime/graphql/index.js @@ -10,7 +10,6 @@ export default function({ loadTestFile }) { // the uptime app and runs it against the live HTTP server, // verifying the pre-loaded documents are returned in a way that // matches the snapshots contained in './fixtures' - loadTestFile(require.resolve('./doc_count')); loadTestFile(require.resolve('./monitor_states')); loadTestFile(require.resolve('./ping_list')); }); diff --git a/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts b/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts new file mode 100644 index 00000000000000..1f5322f581b39d --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts @@ -0,0 +1,20 @@ +/* + * 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'; +import { expectFixtureEql } from '../graphql/helpers/expect_fixture_eql'; +import { REST_API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; + +export default function({ getService }: FtrProviderContext) { + describe('docCount query', () => { + const supertest = getService('supertest'); + + it(`will fetch the index's count`, async () => { + const apiResponse = await supertest.get(REST_API_URLS.INDEX_STATUS); + const data = apiResponse.body; + expectFixtureEql(data, 'doc_count'); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json new file mode 100644 index 00000000000000..41b9af392ddede --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json @@ -0,0 +1,4 @@ +{ + "docCount": 2000, + "indexExists": true +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index 5e26cb9216f458..67b94f19c638ff 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -21,6 +21,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./selected_monitor')); loadTestFile(require.resolve('./ping_histogram')); loadTestFile(require.resolve('./monitor_duration')); + loadTestFile(require.resolve('./doc_count')); }); }); } diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/cloning.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/cloning.ts new file mode 100644 index 00000000000000..512de861e673a3 --- /dev/null +++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/cloning.ts @@ -0,0 +1,200 @@ +/* + * 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 { DeepPartial } from '../../../../../plugins/ml/common/types/common'; +import { DataFrameAnalyticsConfig } from '../../../../../plugins/ml/public/application/data_frame_analytics/common'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + describe('jobs cloning supported by UI form', function() { + this.tags(['smoke']); + + const testDataList: Array<{ + suiteTitle: string; + archive: string; + job: DeepPartial; + }> = (() => { + const timestamp = Date.now(); + + return [ + { + suiteTitle: 'classification job supported by the form', + archive: 'ml/bm_classification', + job: { + id: `bm_1_${timestamp}`, + description: + "Classification job based on 'bank-marketing' dataset with dependentVariable 'y' and trainingPercent '20'", + source: { + index: ['bank-marketing*'], + query: { + match_all: {}, + }, + }, + dest: { + get index(): string { + return `user-bm_1_${timestamp}`; + }, + results_field: 'ml', + }, + analysis: { + classification: { + dependent_variable: 'y', + training_percent: 20, + }, + }, + analyzed_fields: { + includes: [], + excludes: [], + }, + model_memory_limit: '350mb', + allow_lazy_start: false, + }, + }, + { + suiteTitle: 'outlier detection job supported by the form', + archive: 'ml/ihp_outlier', + job: { + id: `ihp_1_${timestamp}`, + description: 'This is the job description', + source: { + index: ['ihp_outlier'], + query: { + match_all: {}, + }, + }, + dest: { + get index(): string { + return `user-ihp_1_${timestamp}`; + }, + results_field: 'ml', + }, + analysis: { + outlier_detection: {}, + }, + analyzed_fields: { + includes: [], + excludes: [], + }, + model_memory_limit: '55mb', + }, + }, + { + suiteTitle: 'regression job supported by the form', + archive: 'ml/egs_regression', + job: { + id: `egs_1_${timestamp}`, + description: 'This is the job description', + source: { + index: ['egs_regression'], + query: { + match_all: {}, + }, + }, + dest: { + get index(): string { + return `user-egs_1_${timestamp}`; + }, + results_field: 'ml', + }, + analysis: { + regression: { + dependent_variable: 'stab', + training_percent: 20, + }, + }, + analyzed_fields: { + includes: [], + excludes: [], + }, + model_memory_limit: '105mb', + }, + }, + ]; + })(); + + before(async () => { + await ml.securityUI.loginAsMlPowerUser(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + for (const testData of testDataList) { + describe(`${testData.suiteTitle}`, function() { + const cloneJobId = `${testData.job.id}_clone`; + const cloneDestIndex = `${testData.job!.dest!.index}_clone`; + + before(async () => { + await esArchiver.load(testData.archive); + await ml.api.createDataFrameAnalyticsJob(testData.job as DataFrameAnalyticsConfig); + + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToDataFrameAnalytics(); + await ml.dataFrameAnalyticsTable.waitForAnalyticsToLoad(); + await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.job.id as string); + await ml.dataFrameAnalyticsTable.cloneJob(testData.job.id as string); + }); + + after(async () => { + await ml.api.deleteIndices(cloneDestIndex); + await ml.api.deleteIndices(testData.job.dest!.index as string); + await esArchiver.unload(testData.archive); + }); + + it('should open the flyout with a proper header', async () => { + expect(await ml.dataFrameAnalyticsCreation.getHeaderText()).to.be( + `Clone job from ${testData.job.id}` + ); + }); + + it('should have correct init form values', async () => { + await ml.dataFrameAnalyticsCreation.assertInitialCloneJobForm( + testData.job as DataFrameAnalyticsConfig + ); + }); + + it('should have disabled Create button on open', async () => { + expect(await ml.dataFrameAnalyticsCreation.isCreateButtonDisabled()).to.be(true); + }); + + it('should enable Create button on a valid form input', async () => { + await ml.dataFrameAnalyticsCreation.setJobId(cloneJobId); + await ml.dataFrameAnalyticsCreation.setDestIndex(cloneDestIndex); + expect(await ml.dataFrameAnalyticsCreation.isCreateButtonDisabled()).to.be(false); + }); + + it('should create a clone job', async () => { + await ml.dataFrameAnalyticsCreation.createAnalyticsJob(); + }); + + it('should start the clone analytics job', async () => { + await ml.dataFrameAnalyticsCreation.assertStartButtonExists(); + await ml.dataFrameAnalyticsCreation.startAnalyticsJob(); + }); + + it('should close the create job flyout', async () => { + await ml.dataFrameAnalyticsCreation.assertCloseButtonExists(); + await ml.dataFrameAnalyticsCreation.closeCreateAnalyticsJobFlyout(); + }); + + it('displays the created job in the analytics table', async () => { + await ml.dataFrameAnalyticsTable.refreshAnalyticsTable(); + await ml.dataFrameAnalyticsTable.filterWithSearchString(cloneJobId); + const rows = await ml.dataFrameAnalyticsTable.parseAnalyticsTable(); + const filteredRows = rows.filter(row => row.id === cloneJobId); + expect(filteredRows).to.have.length( + 1, + `Filtered analytics table should have 1 row for job id '${cloneJobId}' (got matching items '${filteredRows}')` + ); + }); + }); + } + }); +} diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/index.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/index.ts index fda0c5d203f2e0..fe94f4aea9220d 100644 --- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/index.ts +++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/index.ts @@ -12,5 +12,6 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./outlier_detection_creation')); loadTestFile(require.resolve('./regression_creation')); loadTestFile(require.resolve('./classification_creation')); + loadTestFile(require.resolve('./cloning')); }); } diff --git a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts index 405e9575f4222c..f3731f46a5bcee 100644 --- a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts +++ b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts @@ -13,7 +13,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'security']); describe('security', function() { - this.tags(['james']); before(async () => { await esArchiver.load('empty_kibana'); diff --git a/x-pack/test/functional/services/machine_learning/api.ts b/x-pack/test/functional/services/machine_learning/api.ts index 89c81a800e4712..e305d23c1a1249 100644 --- a/x-pack/test/functional/services/machine_learning/api.ts +++ b/x-pack/test/functional/services/machine_learning/api.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; import { ProvidedType } from '@kbn/test/types/ftr'; +import { DataFrameAnalyticsConfig } from '../../../../plugins/ml/public/application/data_frame_analytics/common'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -355,5 +356,26 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { await this.waitForDatafeedState(datafeedConfig.datafeed_id, DATAFEED_STATE.STOPPED); await this.waitForJobState(jobConfig.job_id, JOB_STATE.CLOSED); }, + + async getDataFrameAnalyticsJob(analyticsId: string) { + return await esSupertest.get(`/_ml/data_frame/analytics/${analyticsId}`).expect(200); + }, + + async createDataFrameAnalyticsJob(jobConfig: DataFrameAnalyticsConfig) { + const { id: analyticsId, ...analyticsConfig } = jobConfig; + log.debug(`Creating data frame analytic job with id '${analyticsId}'...`); + await esSupertest + .put(`/_ml/data_frame/analytics/${analyticsId}`) + .send(analyticsConfig) + .expect(200); + + await retry.waitForWithTimeout(`'${analyticsId}' to be created`, 5 * 1000, async () => { + if (await this.getDataFrameAnalyticsJob(analyticsId)) { + return true; + } else { + throw new Error(`expected data frame analytics job '${analyticsId}' to be created`); + } + }); + }, }; } diff --git a/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts b/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts index 96dc8993c3d35e..9d5f5753e8b041 100644 --- a/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts @@ -4,10 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; +import { DataFrameAnalyticsConfig } from '../../../../plugins/ml/public/application/data_frame_analytics/common'; +import { + ClassificationAnalysis, + RegressionAnalysis, +} from '../../../../plugins/ml/public/application/data_frame_analytics/common/analytics'; import { FtrProviderContext } from '../../ftr_provider_context'; import { MlCommon } from './common'; +enum ANALYSIS_CONFIG_TYPE { + OUTLIER_DETECTION = 'outlier_detection', + REGRESSION = 'regression', + CLASSIFICATION = 'classification', +} + +const isRegressionAnalysis = (arg: any): arg is RegressionAnalysis => { + const keys = Object.keys(arg); + return keys.length === 1 && keys[0] === ANALYSIS_CONFIG_TYPE.REGRESSION; +}; + +const isClassificationAnalysis = (arg: any): arg is ClassificationAnalysis => { + const keys = Object.keys(arg); + return keys.length === 1 && keys[0] === ANALYSIS_CONFIG_TYPE.CLASSIFICATION; +}; + export function MachineLearningDataFrameAnalyticsCreationProvider( { getService }: FtrProviderContext, mlCommon: MlCommon @@ -114,6 +135,16 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( ); }, + async assertExcludedFieldsSelection(expectedSelection: string[]) { + const actualSelection = await comboBox.getComboBoxSelectedOptions( + 'mlAnalyticsCreateJobFlyoutExcludesSelect > comboBoxInput' + ); + expect(actualSelection).to.eql( + expectedSelection, + `Excluded fields should be '${expectedSelection}' (got '${actualSelection}')` + ); + }, + async selectSourceIndex(sourceIndex: string) { await comboBox.set( 'mlAnalyticsCreateJobFlyoutSourceIndexSelect > comboBoxInput', @@ -297,6 +328,11 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( await testSubjects.missingOrFail('mlAnalyticsCreateJobFlyoutCreateButton'); }, + async isCreateButtonDisabled() { + const isEnabled = await testSubjects.isEnabled('mlAnalyticsCreateJobFlyoutCreateButton'); + return !isEnabled; + }, + async createAnalyticsJob() { await testSubjects.click('mlAnalyticsCreateJobFlyoutCreateButton'); await retry.tryForTime(5000, async () => { @@ -331,5 +367,24 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( await testSubjects.missingOrFail('mlAnalyticsCreateJobFlyout'); }); }, + + async getHeaderText() { + return await testSubjects.getVisibleText('mlDataFrameAnalyticsFlyoutHeaderTitle'); + }, + + async assertInitialCloneJobForm(job: DataFrameAnalyticsConfig) { + const jobType = Object.keys(job.analysis)[0]; + await this.assertJobTypeSelection(jobType); + await this.assertJobIdValue(''); // id should be empty + await this.assertJobDescriptionValue(String(job.description)); + await this.assertSourceIndexSelection(job.source.index as string[]); + await this.assertDestIndexValue(''); // destination index should be empty + if (isClassificationAnalysis(job.analysis) || isRegressionAnalysis(job.analysis)) { + await this.assertDependentVariableSelection([job.analysis[jobType].dependent_variable]); + await this.assertTrainingPercentValue(String(job.analysis[jobType].training_percent)); + } + await this.assertExcludedFieldsSelection(job.analyzed_fields.excludes); + await this.assertModelMemoryValue(job.model_memory_limit); + }, }; } diff --git a/x-pack/test/functional/services/machine_learning/data_frame_analytics_table.ts b/x-pack/test/functional/services/machine_learning/data_frame_analytics_table.ts index 1d710a1c4cec72..921981768dabaf 100644 --- a/x-pack/test/functional/services/machine_learning/data_frame_analytics_table.ts +++ b/x-pack/test/functional/services/machine_learning/data_frame_analytics_table.ts @@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); + const find = getService('find'); return new (class AnalyticsTable { public async parseAnalyticsTable() { @@ -108,5 +109,18 @@ export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: F const analyticsRow = rows.filter(row => row.id === analyticsId)[0]; expect(analyticsRow).to.eql(expectedRow); } + + public async openRowActions(analyticsId: string) { + await find.clickByCssSelector( + `[data-test-subj="mlAnalyticsTableRow row-${analyticsId}"] [data-test-subj=euiCollapsedItemActionsButton]` + ); + await find.existsByCssSelector('.euiPanel', 20 * 1000); + } + + public async cloneJob(analyticsId: string) { + await this.openRowActions(analyticsId); + await testSubjects.click(`mlAnalyticsJobCloneButton`); + await testSubjects.existOrFail('mlAnalyticsCreateJobFlyout'); + } })(); } diff --git a/x-pack/test/siem_cypress/es_archives/signals/data.json.gz b/x-pack/test/siem_cypress/es_archives/signals/data.json.gz new file mode 100644 index 00000000000000..c0d7fb18bbdb2c Binary files /dev/null and b/x-pack/test/siem_cypress/es_archives/signals/data.json.gz differ diff --git a/x-pack/test/siem_cypress/es_archives/signals/mappings.json b/x-pack/test/siem_cypress/es_archives/signals/mappings.json new file mode 100644 index 00000000000000..114faa0dae3366 --- /dev/null +++ b/x-pack/test/siem_cypress/es_archives/signals/mappings.json @@ -0,0 +1,7602 @@ +{ + "type": "index", + "value": { + "aliases": { + ".siem-signals-default": { + "is_write_index": true + } + }, + "index": ".siem-signals-default-000001", + "mappings": { + "dynamic": "false", + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "doc_values": false, + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "integer" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "signal": { + "properties": { + "ancestors": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_event": { + "properties": { + "action": { + "type": "keyword" + }, + "category": { + "type": "keyword" + }, + "code": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + }, + "module": { + "type": "keyword" + }, + "original": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "outcome": { + "type": "keyword" + }, + "provider": { + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_time": { + "type": "date" + }, + "parent": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "enabled": { + "type": "keyword" + }, + "false_positives": { + "type": "keyword" + }, + "filters": { + "type": "object" + }, + "from": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "immutable": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "language": { + "type": "keyword" + }, + "max_signals": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "output_index": { + "type": "keyword" + }, + "query": { + "type": "keyword" + }, + "references": { + "type": "keyword" + }, + "risk_score": { + "type": "keyword" + }, + "rule_id": { + "type": "keyword" + }, + "saved_id": { + "type": "keyword" + }, + "severity": { + "type": "keyword" + }, + "size": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + } + } + }, + "timeline_id": { + "type": "keyword" + }, + "timeline_title": { + "type": "keyword" + }, + "to": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": ".siem-signals-default", + "rollover_alias": ".siem-signals-default" + }, + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + "auditbeat-7.6.0": { + "is_write_index": true + } + }, + "index": "auditbeat-7.6.0-2020.03.11-000001", + "mappings": { + "_meta": { + "beat": "auditbeat", + "version": "7.6.0" + }, + "date_detection": false, + "dynamic_templates": [ + { + "labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "container.labels.*" + } + }, + { + "dns.answers": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "dns.answers.*" + } + }, + { + "log.syslog": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "log.syslog.*" + } + }, + { + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, + { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, + { + "kubernetes.labels.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.labels.*" + } + }, + { + "kubernetes.annotations.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.annotations.*" + } + }, + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "auditd": { + "properties": { + "data": { + "properties": { + "a0": { + "ignore_above": 1024, + "type": "keyword" + }, + "a1": { + "ignore_above": 1024, + "type": "keyword" + }, + "a2": { + "ignore_above": 1024, + "type": "keyword" + }, + "a3": { + "ignore_above": 1024, + "type": "keyword" + }, + "a[0-3]": { + "ignore_above": 1024, + "type": "keyword" + }, + "acct": { + "ignore_above": 1024, + "type": "keyword" + }, + "acl": { + "ignore_above": 1024, + "type": "keyword" + }, + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "added": { + "ignore_above": 1024, + "type": "keyword" + }, + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "apparmor": { + "ignore_above": 1024, + "type": "keyword" + }, + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "argc": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_limit": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_wait_time": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_failure": { + "ignore_above": 1024, + "type": "keyword" + }, + "banners": { + "ignore_above": 1024, + "type": "keyword" + }, + "bool": { + "ignore_above": 1024, + "type": "keyword" + }, + "bus": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "capability": { + "ignore_above": 1024, + "type": "keyword" + }, + "cgroup": { + "ignore_above": 1024, + "type": "keyword" + }, + "changed": { + "ignore_above": 1024, + "type": "keyword" + }, + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "cmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "compat": { + "ignore_above": 1024, + "type": "keyword" + }, + "daddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "default-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "dmac": { + "ignore_above": 1024, + "type": "keyword" + }, + "dport": { + "ignore_above": 1024, + "type": "keyword" + }, + "enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "entries": { + "ignore_above": 1024, + "type": "keyword" + }, + "exit": { + "ignore_above": 1024, + "type": "keyword" + }, + "fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "fd": { + "ignore_above": 1024, + "type": "keyword" + }, + "fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "feature": { + "ignore_above": 1024, + "type": "keyword" + }, + "fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "file": { + "ignore_above": 1024, + "type": "keyword" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "format": { + "ignore_above": 1024, + "type": "keyword" + }, + "fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "grantors": { + "ignore_above": 1024, + "type": "keyword" + }, + "grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "hook": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "icmp_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "igid": { + "ignore_above": 1024, + "type": "keyword" + }, + "img-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "inif": { + "ignore_above": 1024, + "type": "keyword" + }, + "ino": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "invalid_context": { + "ignore_above": 1024, + "type": "keyword" + }, + "ioctlcmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipx-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "items": { + "ignore_above": 1024, + "type": "keyword" + }, + "iuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "ksize": { + "ignore_above": 1024, + "type": "keyword" + }, + "laddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "len": { + "ignore_above": 1024, + "type": "keyword" + }, + "list": { + "ignore_above": 1024, + "type": "keyword" + }, + "lport": { + "ignore_above": 1024, + "type": "keyword" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "macproto": { + "ignore_above": 1024, + "type": "keyword" + }, + "maj": { + "ignore_above": 1024, + "type": "keyword" + }, + "major": { + "ignore_above": 1024, + "type": "keyword" + }, + "minor": { + "ignore_above": 1024, + "type": "keyword" + }, + "model": { + "ignore_above": 1024, + "type": "keyword" + }, + "msg": { + "ignore_above": 1024, + "type": "keyword" + }, + "nargs": { + "ignore_above": 1024, + "type": "keyword" + }, + "net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ocomm": { + "ignore_above": 1024, + "type": "keyword" + }, + "oflag": { + "ignore_above": 1024, + "type": "keyword" + }, + "old": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-auid": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_val": { + "ignore_above": 1024, + "type": "keyword" + }, + "op": { + "ignore_above": 1024, + "type": "keyword" + }, + "opid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oses": { + "ignore_above": 1024, + "type": "keyword" + }, + "outif": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "ignore_above": 1024, + "type": "keyword" + }, + "per": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm_mask": { + "ignore_above": 1024, + "type": "keyword" + }, + "permissive": { + "ignore_above": 1024, + "type": "keyword" + }, + "pfs": { + "ignore_above": 1024, + "type": "keyword" + }, + "printer": { + "ignore_above": 1024, + "type": "keyword" + }, + "prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "proto": { + "ignore_above": 1024, + "type": "keyword" + }, + "qbytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "range": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "removed": { + "ignore_above": 1024, + "type": "keyword" + }, + "res": { + "ignore_above": 1024, + "type": "keyword" + }, + "resrc": { + "ignore_above": 1024, + "type": "keyword" + }, + "rport": { + "ignore_above": 1024, + "type": "keyword" + }, + "sauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "scontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "selected-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperm": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperms": { + "ignore_above": 1024, + "type": "keyword" + }, + "seqno": { + "ignore_above": 1024, + "type": "keyword" + }, + "seresult": { + "ignore_above": 1024, + "type": "keyword" + }, + "ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "sig": { + "ignore_above": 1024, + "type": "keyword" + }, + "sigev_signo": { + "ignore_above": 1024, + "type": "keyword" + }, + "smac": { + "ignore_above": 1024, + "type": "keyword" + }, + "socket": { + "properties": { + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "ignore_above": 1024, + "type": "keyword" + }, + "saddr": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "spid": { + "ignore_above": 1024, + "type": "keyword" + }, + "sport": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "subj": { + "ignore_above": 1024, + "type": "keyword" + }, + "success": { + "ignore_above": 1024, + "type": "keyword" + }, + "syscall": { + "ignore_above": 1024, + "type": "keyword" + }, + "table": { + "ignore_above": 1024, + "type": "keyword" + }, + "tclass": { + "ignore_above": 1024, + "type": "keyword" + }, + "tcontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "ignore_above": 1024, + "type": "keyword" + }, + "unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "uri": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "val": { + "ignore_above": 1024, + "type": "keyword" + }, + "ver": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "watch": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "paths": { + "properties": { + "dev": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "item": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "nametype": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_role": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_user": { + "ignore_above": 1024, + "type": "keyword" + }, + "objtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "ogid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ouid": { + "ignore_above": 1024, + "type": "keyword" + }, + "rdev": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "result": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "session": { + "ignore_above": 1024, + "type": "keyword" + }, + "summary": { + "properties": { + "actor": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "how": { + "ignore_above": 1024, + "type": "keyword" + }, + "object": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "labels": { + "type": "object" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "fields": { + "type": "object" + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "fields": { + "raw": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "selinux": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "setgid": { + "type": "boolean" + }, + "setuid": { + "type": "boolean" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geoip": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "jolokia": { + "properties": { + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "secured": { + "type": "boolean" + }, + "server": { + "properties": { + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kubernetes": { + "properties": { + "annotations": { + "properties": { + "*": { + "type": "object" + } + } + }, + "container": { + "properties": { + "image": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "deployment": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pod": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "replicaset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "statefulset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "socket": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "system": { + "properties": { + "audit": { + "properties": { + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "boottime": { + "type": "date" + }, + "containerized": { + "type": "boolean" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timezone": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "offset": { + "properties": { + "sec": { + "type": "long" + } + } + } + } + }, + "uptime": { + "type": "long" + } + } + }, + "package": { + "properties": { + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "installtime": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "release": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "summary": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "properties": { + "last_changed": { + "type": "date" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "shell": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_information": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "timeseries": { + "properties": { + "instance": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tracing": { + "properties": { + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "audit": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "effective": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "filesystem": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name_map": { + "type": "object" + }, + "saved": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "selinux": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": "auditbeat", + "rollover_alias": "auditbeat-7.6.0" + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "1", + "number_of_shards": "1", + "query": { + "default_field": [ + "message", + "tags", + "agent.ephemeral_id", + "agent.id", + "agent.name", + "agent.type", + "agent.version", + "as.organization.name", + "client.address", + "client.as.organization.name", + "client.domain", + "client.geo.city_name", + "client.geo.continent_name", + "client.geo.country_iso_code", + "client.geo.country_name", + "client.geo.name", + "client.geo.region_iso_code", + "client.geo.region_name", + "client.mac", + "client.registered_domain", + "client.top_level_domain", + "client.user.domain", + "client.user.email", + "client.user.full_name", + "client.user.group.domain", + "client.user.group.id", + "client.user.group.name", + "client.user.hash", + "client.user.id", + "client.user.name", + "cloud.account.id", + "cloud.availability_zone", + "cloud.instance.id", + "cloud.instance.name", + "cloud.machine.type", + "cloud.provider", + "cloud.region", + "container.id", + "container.image.name", + "container.image.tag", + "container.name", + "container.runtime", + "destination.address", + "destination.as.organization.name", + "destination.domain", + "destination.geo.city_name", + "destination.geo.continent_name", + "destination.geo.country_iso_code", + "destination.geo.country_name", + "destination.geo.name", + "destination.geo.region_iso_code", + "destination.geo.region_name", + "destination.mac", + "destination.registered_domain", + "destination.top_level_domain", + "destination.user.domain", + "destination.user.email", + "destination.user.full_name", + "destination.user.group.domain", + "destination.user.group.id", + "destination.user.group.name", + "destination.user.hash", + "destination.user.id", + "destination.user.name", + "dns.answers.class", + "dns.answers.data", + "dns.answers.name", + "dns.answers.type", + "dns.header_flags", + "dns.id", + "dns.op_code", + "dns.question.class", + "dns.question.name", + "dns.question.registered_domain", + "dns.question.subdomain", + "dns.question.top_level_domain", + "dns.question.type", + "dns.response_code", + "dns.type", + "ecs.version", + "error.code", + "error.id", + "error.message", + "error.stack_trace", + "error.type", + "event.action", + "event.category", + "event.code", + "event.dataset", + "event.hash", + "event.id", + "event.kind", + "event.module", + "event.original", + "event.outcome", + "event.provider", + "event.timezone", + "event.type", + "file.device", + "file.directory", + "file.extension", + "file.gid", + "file.group", + "file.hash.md5", + "file.hash.sha1", + "file.hash.sha256", + "file.hash.sha512", + "file.inode", + "file.mode", + "file.name", + "file.owner", + "file.path", + "file.target_path", + "file.type", + "file.uid", + "geo.city_name", + "geo.continent_name", + "geo.country_iso_code", + "geo.country_name", + "geo.name", + "geo.region_iso_code", + "geo.region_name", + "group.domain", + "group.id", + "group.name", + "hash.md5", + "hash.sha1", + "hash.sha256", + "hash.sha512", + "host.architecture", + "host.geo.city_name", + "host.geo.continent_name", + "host.geo.country_iso_code", + "host.geo.country_name", + "host.geo.name", + "host.geo.region_iso_code", + "host.geo.region_name", + "host.hostname", + "host.id", + "host.mac", + "host.name", + "host.os.family", + "host.os.full", + "host.os.kernel", + "host.os.name", + "host.os.platform", + "host.os.version", + "host.type", + "host.user.domain", + "host.user.email", + "host.user.full_name", + "host.user.group.domain", + "host.user.group.id", + "host.user.group.name", + "host.user.hash", + "host.user.id", + "host.user.name", + "http.request.body.content", + "http.request.method", + "http.request.referrer", + "http.response.body.content", + "http.version", + "log.level", + "log.logger", + "log.origin.file.name", + "log.origin.function", + "log.original", + "log.syslog.facility.name", + "log.syslog.severity.name", + "network.application", + "network.community_id", + "network.direction", + "network.iana_number", + "network.name", + "network.protocol", + "network.transport", + "network.type", + "observer.geo.city_name", + "observer.geo.continent_name", + "observer.geo.country_iso_code", + "observer.geo.country_name", + "observer.geo.name", + "observer.geo.region_iso_code", + "observer.geo.region_name", + "observer.hostname", + "observer.mac", + "observer.name", + "observer.os.family", + "observer.os.full", + "observer.os.kernel", + "observer.os.name", + "observer.os.platform", + "observer.os.version", + "observer.product", + "observer.serial_number", + "observer.type", + "observer.vendor", + "observer.version", + "organization.id", + "organization.name", + "os.family", + "os.full", + "os.kernel", + "os.name", + "os.platform", + "os.version", + "package.architecture", + "package.checksum", + "package.description", + "package.install_scope", + "package.license", + "package.name", + "package.path", + "package.version", + "process.args", + "text", + "process.executable", + "process.hash.md5", + "process.hash.sha1", + "process.hash.sha256", + "process.hash.sha512", + "process.name", + "text", + "text", + "text", + "text", + "text", + "process.thread.name", + "process.title", + "process.working_directory", + "server.address", + "server.as.organization.name", + "server.domain", + "server.geo.city_name", + "server.geo.continent_name", + "server.geo.country_iso_code", + "server.geo.country_name", + "server.geo.name", + "server.geo.region_iso_code", + "server.geo.region_name", + "server.mac", + "server.registered_domain", + "server.top_level_domain", + "server.user.domain", + "server.user.email", + "server.user.full_name", + "server.user.group.domain", + "server.user.group.id", + "server.user.group.name", + "server.user.hash", + "server.user.id", + "server.user.name", + "service.ephemeral_id", + "service.id", + "service.name", + "service.node.name", + "service.state", + "service.type", + "service.version", + "source.address", + "source.as.organization.name", + "source.domain", + "source.geo.city_name", + "source.geo.continent_name", + "source.geo.country_iso_code", + "source.geo.country_name", + "source.geo.name", + "source.geo.region_iso_code", + "source.geo.region_name", + "source.mac", + "source.registered_domain", + "source.top_level_domain", + "source.user.domain", + "source.user.email", + "source.user.full_name", + "source.user.group.domain", + "source.user.group.id", + "source.user.group.name", + "source.user.hash", + "source.user.id", + "source.user.name", + "threat.framework", + "threat.tactic.id", + "threat.tactic.name", + "threat.tactic.reference", + "threat.technique.id", + "threat.technique.name", + "threat.technique.reference", + "tracing.trace.id", + "tracing.transaction.id", + "url.domain", + "url.extension", + "url.fragment", + "url.full", + "url.original", + "url.password", + "url.path", + "url.query", + "url.registered_domain", + "url.scheme", + "url.top_level_domain", + "url.username", + "user.domain", + "user.email", + "user.full_name", + "user.group.domain", + "user.group.id", + "user.group.name", + "user.hash", + "user.id", + "user.name", + "user_agent.device.name", + "user_agent.name", + "text", + "user_agent.original", + "user_agent.os.family", + "user_agent.os.full", + "user_agent.os.kernel", + "user_agent.os.name", + "user_agent.os.platform", + "user_agent.os.version", + "user_agent.version", + "text", + "agent.hostname", + "timeseries.instance", + "cloud.project.id", + "cloud.image.id", + "host.os.build", + "host.os.codename", + "kubernetes.pod.name", + "kubernetes.pod.uid", + "kubernetes.namespace", + "kubernetes.node.name", + "kubernetes.replicaset.name", + "kubernetes.deployment.name", + "kubernetes.statefulset.name", + "kubernetes.container.name", + "kubernetes.container.image", + "jolokia.agent.version", + "jolokia.agent.id", + "jolokia.server.product", + "jolokia.server.version", + "jolokia.server.vendor", + "jolokia.url", + "raw", + "file.origin", + "file.selinux.user", + "file.selinux.role", + "file.selinux.domain", + "file.selinux.level", + "user.audit.id", + "user.audit.name", + "user.effective.id", + "user.effective.name", + "user.effective.group.id", + "user.effective.group.name", + "user.filesystem.id", + "user.filesystem.name", + "user.filesystem.group.id", + "user.filesystem.group.name", + "user.saved.id", + "user.saved.name", + "user.saved.group.id", + "user.saved.group.name", + "user.selinux.user", + "user.selinux.role", + "user.selinux.domain", + "user.selinux.level", + "user.selinux.category", + "source.path", + "destination.path", + "auditd.message_type", + "auditd.session", + "auditd.result", + "auditd.summary.actor.primary", + "auditd.summary.actor.secondary", + "auditd.summary.object.type", + "auditd.summary.object.primary", + "auditd.summary.object.secondary", + "auditd.summary.how", + "auditd.paths.inode", + "auditd.paths.dev", + "auditd.paths.obj_user", + "auditd.paths.obj_role", + "auditd.paths.obj_domain", + "auditd.paths.obj_level", + "auditd.paths.objtype", + "auditd.paths.ouid", + "auditd.paths.rdev", + "auditd.paths.nametype", + "auditd.paths.ogid", + "auditd.paths.item", + "auditd.paths.mode", + "auditd.paths.name", + "auditd.data.action", + "auditd.data.minor", + "auditd.data.acct", + "auditd.data.addr", + "auditd.data.cipher", + "auditd.data.id", + "auditd.data.entries", + "auditd.data.kind", + "auditd.data.ksize", + "auditd.data.spid", + "auditd.data.arch", + "auditd.data.argc", + "auditd.data.major", + "auditd.data.unit", + "auditd.data.table", + "auditd.data.terminal", + "auditd.data.grantors", + "auditd.data.direction", + "auditd.data.op", + "auditd.data.tty", + "auditd.data.syscall", + "auditd.data.data", + "auditd.data.family", + "auditd.data.mac", + "auditd.data.pfs", + "auditd.data.items", + "auditd.data.a0", + "auditd.data.a1", + "auditd.data.a2", + "auditd.data.a3", + "auditd.data.hostname", + "auditd.data.lport", + "auditd.data.rport", + "auditd.data.exit", + "auditd.data.fp", + "auditd.data.laddr", + "auditd.data.sport", + "auditd.data.capability", + "auditd.data.nargs", + "auditd.data.new-enabled", + "auditd.data.audit_backlog_limit", + "auditd.data.dir", + "auditd.data.cap_pe", + "auditd.data.model", + "auditd.data.new_pp", + "auditd.data.old-enabled", + "auditd.data.oauid", + "auditd.data.old", + "auditd.data.banners", + "auditd.data.feature", + "auditd.data.vm-ctx", + "auditd.data.opid", + "auditd.data.seperms", + "auditd.data.seresult", + "auditd.data.new-rng", + "auditd.data.old-net", + "auditd.data.sigev_signo", + "auditd.data.ino", + "auditd.data.old_enforcing", + "auditd.data.old-vcpu", + "auditd.data.range", + "auditd.data.res", + "auditd.data.added", + "auditd.data.fam", + "auditd.data.nlnk-pid", + "auditd.data.subj", + "auditd.data.a[0-3]", + "auditd.data.cgroup", + "auditd.data.kernel", + "auditd.data.ocomm", + "auditd.data.new-net", + "auditd.data.permissive", + "auditd.data.class", + "auditd.data.compat", + "auditd.data.fi", + "auditd.data.changed", + "auditd.data.msg", + "auditd.data.dport", + "auditd.data.new-seuser", + "auditd.data.invalid_context", + "auditd.data.dmac", + "auditd.data.ipx-net", + "auditd.data.iuid", + "auditd.data.macproto", + "auditd.data.obj", + "auditd.data.ipid", + "auditd.data.new-fs", + "auditd.data.vm-pid", + "auditd.data.cap_pi", + "auditd.data.old-auid", + "auditd.data.oses", + "auditd.data.fd", + "auditd.data.igid", + "auditd.data.new-disk", + "auditd.data.parent", + "auditd.data.len", + "auditd.data.oflag", + "auditd.data.uuid", + "auditd.data.code", + "auditd.data.nlnk-grp", + "auditd.data.cap_fp", + "auditd.data.new-mem", + "auditd.data.seperm", + "auditd.data.enforcing", + "auditd.data.new-chardev", + "auditd.data.old-rng", + "auditd.data.outif", + "auditd.data.cmd", + "auditd.data.hook", + "auditd.data.new-level", + "auditd.data.sauid", + "auditd.data.sig", + "auditd.data.audit_backlog_wait_time", + "auditd.data.printer", + "auditd.data.old-mem", + "auditd.data.perm", + "auditd.data.old_pi", + "auditd.data.state", + "auditd.data.format", + "auditd.data.new_gid", + "auditd.data.tcontext", + "auditd.data.maj", + "auditd.data.watch", + "auditd.data.device", + "auditd.data.grp", + "auditd.data.bool", + "auditd.data.icmp_type", + "auditd.data.new_lock", + "auditd.data.old_prom", + "auditd.data.acl", + "auditd.data.ip", + "auditd.data.new_pi", + "auditd.data.default-context", + "auditd.data.inode_gid", + "auditd.data.new-log_passwd", + "auditd.data.new_pe", + "auditd.data.selected-context", + "auditd.data.cap_fver", + "auditd.data.file", + "auditd.data.net", + "auditd.data.virt", + "auditd.data.cap_pp", + "auditd.data.old-range", + "auditd.data.resrc", + "auditd.data.new-range", + "auditd.data.obj_gid", + "auditd.data.proto", + "auditd.data.old-disk", + "auditd.data.audit_failure", + "auditd.data.inif", + "auditd.data.vm", + "auditd.data.flags", + "auditd.data.nlnk-fam", + "auditd.data.old-fs", + "auditd.data.old-ses", + "auditd.data.seqno", + "auditd.data.fver", + "auditd.data.qbytes", + "auditd.data.seuser", + "auditd.data.cap_fe", + "auditd.data.new-vcpu", + "auditd.data.old-level", + "auditd.data.old_pp", + "auditd.data.daddr", + "auditd.data.old-role", + "auditd.data.ioctlcmd", + "auditd.data.smac", + "auditd.data.apparmor", + "auditd.data.fe", + "auditd.data.perm_mask", + "auditd.data.ses", + "auditd.data.cap_fi", + "auditd.data.obj_uid", + "auditd.data.reason", + "auditd.data.list", + "auditd.data.old_lock", + "auditd.data.bus", + "auditd.data.old_pe", + "auditd.data.new-role", + "auditd.data.prom", + "auditd.data.uri", + "auditd.data.audit_enabled", + "auditd.data.old-log_passwd", + "auditd.data.old-seuser", + "auditd.data.per", + "auditd.data.scontext", + "auditd.data.tclass", + "auditd.data.ver", + "auditd.data.new", + "auditd.data.val", + "auditd.data.img-ctx", + "auditd.data.old-chardev", + "auditd.data.old_val", + "auditd.data.success", + "auditd.data.inode_uid", + "auditd.data.removed", + "auditd.data.socket.port", + "auditd.data.socket.saddr", + "auditd.data.socket.addr", + "auditd.data.socket.family", + "auditd.data.socket.path", + "geoip.continent_name", + "geoip.city_name", + "geoip.region_name", + "geoip.country_iso_code", + "hash.blake2b_256", + "hash.blake2b_384", + "hash.blake2b_512", + "hash.md5", + "hash.sha1", + "hash.sha224", + "hash.sha256", + "hash.sha384", + "hash.sha3_224", + "hash.sha3_256", + "hash.sha3_384", + "hash.sha3_512", + "hash.sha512", + "hash.sha512_224", + "hash.sha512_256", + "hash.xxh64", + "event.origin", + "user.entity_id", + "user.terminal", + "process.entity_id", + "process.hash.blake2b_256", + "process.hash.blake2b_384", + "process.hash.blake2b_512", + "process.hash.sha224", + "process.hash.sha384", + "process.hash.sha3_224", + "process.hash.sha3_256", + "process.hash.sha3_384", + "process.hash.sha3_512", + "process.hash.sha512_224", + "process.hash.sha512_256", + "process.hash.xxh64", + "socket.entity_id", + "system.audit.host.timezone.name", + "system.audit.host.hostname", + "system.audit.host.id", + "system.audit.host.architecture", + "system.audit.host.mac", + "system.audit.host.os.codename", + "system.audit.host.os.platform", + "system.audit.host.os.name", + "system.audit.host.os.family", + "system.audit.host.os.version", + "system.audit.host.os.kernel", + "system.audit.package.entity_id", + "system.audit.package.name", + "system.audit.package.version", + "system.audit.package.release", + "system.audit.package.arch", + "system.audit.package.license", + "system.audit.package.summary", + "system.audit.package.url", + "system.audit.user.name", + "system.audit.user.uid", + "system.audit.user.gid", + "system.audit.user.dir", + "system.audit.user.shell", + "system.audit.user.user_information", + "system.audit.user.password.type", + "fields.*" + ] + }, + "refresh_interval": "5s" + } + } + } +} \ No newline at end of file