From b92d955b5648bc24916e2d9f7e5f65d568daa734 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Wed, 30 Jun 2021 00:27:58 -0700 Subject: [PATCH 1/8] [SECURITY] Adds security links to doc link service (#102676) --- ...-plugin-core-public.doclinksstart.links.md | 11 ++++++++++ ...kibana-plugin-core-public.doclinksstart.md | 2 +- .../public/doc_links/doc_links_service.ts | 22 +++++++++++++++++++ src/core/public/public.api.md | 11 ++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 8754d19e2fc13..3650fe970d8fc 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -28,9 +28,13 @@ readonly links: { readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; + readonly suricataModule: string; + readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; + readonly auditdModule: string; + readonly systemModule: string; }; readonly metricbeat: { readonly base: string; @@ -47,6 +51,9 @@ readonly links: { readonly heartbeat: { readonly base: string; }; + readonly libbeat: { + readonly getStarted: string; + }; readonly logstash: { readonly base: string; }; @@ -123,6 +130,10 @@ readonly links: { readonly siem: { readonly guide: string; readonly gettingStarted: string; + readonly ml: string; + readonly ruleChangeLog: string; + readonly detectionsReq: string; + readonly networkMap: string; }; readonly query: { readonly eql: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 7dbb8a5869485..4f66cc9a2c10f 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
}>;
readonly ecs: {
readonly guide: string;
};
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
}>;
readonly ecs: {
readonly guide: string;
};
} | | diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index fcc12f43ec531..1efe1e560bce1 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -51,9 +51,13 @@ export class DocLinksService { elasticsearchOutput: `${ELASTIC_WEBSITE_URL}guide/en/beats/filebeat/${DOC_LINK_VERSION}/elasticsearch-output.html`, startup: `${ELASTIC_WEBSITE_URL}guide/en/beats/filebeat/${DOC_LINK_VERSION}/filebeat-starting.html`, exportedFields: `${ELASTIC_WEBSITE_URL}guide/en/beats/filebeat/${DOC_LINK_VERSION}/exported-fields.html`, + suricataModule: `${ELASTIC_WEBSITE_URL}guide/en/beats/filebeat/${DOC_LINK_VERSION}/filebeat-module-suricata.html`, + zeekModule: `${ELASTIC_WEBSITE_URL}guide/en/beats/filebeat/${DOC_LINK_VERSION}/filebeat-module-zeek.html`, }, auditbeat: { base: `${ELASTIC_WEBSITE_URL}guide/en/beats/auditbeat/${DOC_LINK_VERSION}`, + auditdModule: `${ELASTIC_WEBSITE_URL}guide/en/beats/auditbeat/${DOC_LINK_VERSION}/auditbeat-module-auditd.html`, + systemModule: `${ELASTIC_WEBSITE_URL}guide/en/beats/auditbeat/${DOC_LINK_VERSION}/auditbeat-module-system.html`, }, enterpriseSearch: { base: `${ELASTIC_WEBSITE_URL}guide/en/enterprise-search/${DOC_LINK_VERSION}`, @@ -70,6 +74,9 @@ export class DocLinksService { heartbeat: { base: `${ELASTIC_WEBSITE_URL}guide/en/beats/heartbeat/${DOC_LINK_VERSION}`, }, + libbeat: { + getStarted: `${ELASTIC_WEBSITE_URL}guide/en/beats/libbeat/${DOC_LINK_VERSION}/getting-started.html`, + }, logstash: { base: `${ELASTIC_WEBSITE_URL}guide/en/logstash/${DOC_LINK_VERSION}`, }, @@ -195,6 +202,10 @@ export class DocLinksService { siem: { guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`, gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`, + ml: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/machine-learning.html`, + ruleChangeLog: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/prebuilt-rules-changelog.html`, + detectionsReq: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/detections-permissions-section.html`, + networkMap: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/conf-map-ui.html`, }, query: { eql: `${ELASTICSEARCH_DOCS}eql.html`, @@ -451,9 +462,13 @@ export interface DocLinksStart { readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; + readonly suricataModule: string; + readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; + readonly auditdModule: string; + readonly systemModule: string; }; readonly metricbeat: { readonly base: string; @@ -470,6 +485,9 @@ export interface DocLinksStart { readonly heartbeat: { readonly base: string; }; + readonly libbeat: { + readonly getStarted: string; + }; readonly logstash: { readonly base: string; }; @@ -546,6 +564,10 @@ export interface DocLinksStart { readonly siem: { readonly guide: string; readonly gettingStarted: string; + readonly ml: string; + readonly ruleChangeLog: string; + readonly detectionsReq: string; + readonly networkMap: string; }; readonly query: { readonly eql: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 3f8184bea97fe..f18dfb02fd41d 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -507,9 +507,13 @@ export interface DocLinksStart { readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; + readonly suricataModule: string; + readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; + readonly auditdModule: string; + readonly systemModule: string; }; readonly metricbeat: { readonly base: string; @@ -526,6 +530,9 @@ export interface DocLinksStart { readonly heartbeat: { readonly base: string; }; + readonly libbeat: { + readonly getStarted: string; + }; readonly logstash: { readonly base: string; }; @@ -602,6 +609,10 @@ export interface DocLinksStart { readonly siem: { readonly guide: string; readonly gettingStarted: string; + readonly ml: string; + readonly ruleChangeLog: string; + readonly detectionsReq: string; + readonly networkMap: string; }; readonly query: { readonly eql: string; From 790bd35ea7716e6de03db35c382abfb83c39543a Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 30 Jun 2021 10:54:06 +0300 Subject: [PATCH 2/8] [TSVB] Fix TSVB is not reporting all categories of Elasticsearch error (#102926) * [TSVB] Fix TSVB is not reporting all categories of Elasticsearch error Closes: #94182 * move validation to server side Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/fields_utils.ts | 2 +- .../lib => common}/validate_interval.ts | 41 ++++---- .../components/timeseries_visualization.tsx | 12 --- .../application/components/vis_editor.tsx | 4 +- .../public/request_handler.ts | 13 +-- .../server/lib/get_vis_data.ts | 36 ++++++- .../get_interval_and_timefield.test.ts | 9 +- .../vis_data/get_interval_and_timefield.ts | 25 ++++- .../server/lib/vis_data/get_series_data.ts | 17 ++-- .../server/lib/vis_data/get_table_data.ts | 32 ++---- .../lib/vis_data/handle_error_response.js | 37 ------- .../vis_data/handle_error_response.test.ts | 99 +++++++++++++++++++ .../lib/vis_data/handle_error_response.ts | 57 +++++++++++ .../series/date_histogram.test.js | 11 ++- .../lib/vis_data/series/get_request_params.ts | 16 +-- .../vis_type_timeseries/server/types.ts | 16 ++- 16 files changed, 285 insertions(+), 142 deletions(-) rename src/plugins/vis_type_timeseries/{public/application/lib => common}/validate_interval.ts (52%) delete mode 100644 src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.js create mode 100644 src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.test.ts create mode 100644 src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.ts diff --git a/src/plugins/vis_type_timeseries/common/fields_utils.ts b/src/plugins/vis_type_timeseries/common/fields_utils.ts index 6a83dd323b3fd..b64fcc383a1bb 100644 --- a/src/plugins/vis_type_timeseries/common/fields_utils.ts +++ b/src/plugins/vis_type_timeseries/common/fields_utils.ts @@ -25,7 +25,7 @@ export class FieldNotFoundError extends Error { return this.constructor.name; } - public get body() { + public get errBody() { return this.message; } } diff --git a/src/plugins/vis_type_timeseries/public/application/lib/validate_interval.ts b/src/plugins/vis_type_timeseries/common/validate_interval.ts similarity index 52% rename from src/plugins/vis_type_timeseries/public/application/lib/validate_interval.ts rename to src/plugins/vis_type_timeseries/common/validate_interval.ts index a602b34d99986..7f9ccf20c0eb1 100644 --- a/src/plugins/vis_type_timeseries/public/application/lib/validate_interval.ts +++ b/src/plugins/vis_type_timeseries/common/validate_interval.ts @@ -7,20 +7,29 @@ */ import { i18n } from '@kbn/i18n'; -import { GTE_INTERVAL_RE } from '../../../common/interval_regexp'; -import { search } from '../../../../../plugins/data/public'; +import { GTE_INTERVAL_RE } from './interval_regexp'; +import { parseInterval, TimeRangeBounds } from '../../data/common'; -import type { TimeRangeBounds } from '../../../../data/common'; -import type { TimeseriesVisParams } from '../../types'; +export class ValidateIntervalError extends Error { + constructor() { + super( + i18n.translate('visTypeTimeseries.validateInterval.notifier.maxBucketsExceededErrorMessage', { + defaultMessage: + 'Your query attempted to fetch too much data. Reducing the time range or changing the interval used usually fixes the issue.', + }) + ); + } + + public get name() { + return this.constructor.name; + } -const { parseInterval } = search.aggs; + public get errBody() { + return this.message; + } +} -export function validateInterval( - bounds: TimeRangeBounds, - panel: TimeseriesVisParams, - maxBuckets: number -) { - const { interval } = panel; +export function validateInterval(bounds: TimeRangeBounds, interval: string, maxBuckets: number) { const { min, max } = bounds; // No need to check auto it will return around 100 if (!interval) return; @@ -33,15 +42,7 @@ export function validateInterval( const span = max!.valueOf() - min!.valueOf(); const buckets = Math.floor(span / duration.asMilliseconds()); if (buckets > maxBuckets) { - throw new Error( - i18n.translate( - 'visTypeTimeseries.validateInterval.notifier.maxBucketsExceededErrorMessage', - { - defaultMessage: - 'Your query attempted to fetch too much data. Reducing the time range or changing the interval used usually fixes the issue.', - } - ) - ); + throw new ValidateIntervalError(); } } } diff --git a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx b/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx index 5391bf319ee57..d97100a0cfaaf 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx @@ -18,8 +18,6 @@ import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { PersistedState } from 'src/plugins/visualizations/public'; import { PaletteRegistry } from 'src/plugins/charts/public'; -// @ts-expect-error -import { ErrorComponent } from './error'; import { TimeseriesVisTypes } from './vis_types'; import type { TimeseriesVisData, PanelData } from '../../../common/types'; import { isVisSeriesData } from '../../../common/vis_data_utils'; @@ -147,16 +145,6 @@ function TimeseriesVisualization({ handlers.done(); }); - // Show the error panel - const error = isVisSeriesData(visData) && visData[model.id]?.error; - if (error) { - return ( -
- -
- ); - } - const VisComponent = TimeseriesVisTypes[model.type]; const isLastValueMode = diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx index 99f2564328402..d11b5a60b31b7 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx @@ -159,8 +159,8 @@ export class VisEditor extends Component { - this.visDataSubject.next(visData); + onDataChange = (data: { visData?: TimeseriesVisData }) => { + this.visDataSubject.next(data?.visData); }; render() { diff --git a/src/plugins/vis_type_timeseries/public/request_handler.ts b/src/plugins/vis_type_timeseries/public/request_handler.ts index 89b3cb3b6c583..4cd297a597dfc 100644 --- a/src/plugins/vis_type_timeseries/public/request_handler.ts +++ b/src/plugins/vis_type_timeseries/public/request_handler.ts @@ -6,14 +6,13 @@ * Side Public License, v 1. */ -import { KibanaContext } from '../../data/public'; - import { getTimezone } from './application/lib/get_timezone'; -import { validateInterval } from './application/lib/validate_interval'; import { getUISettings, getDataStart, getCoreStart } from './services'; -import { MAX_BUCKETS_SETTING, ROUTES } from '../common/constants'; -import { TimeseriesVisParams } from './types'; +import { ROUTES } from '../common/constants'; + +import type { TimeseriesVisParams } from './types'; import type { TimeseriesVisData } from '../common/types'; +import type { KibanaContext } from '../../data/public'; interface MetricsRequestHandlerParams { input: KibanaContext | null; @@ -37,10 +36,6 @@ export const metricsRequestHandler = async ({ const parsedTimeRange = data.query.timefilter.timefilter.calculateBounds(input?.timeRange!); if (visParams && visParams.id && !visParams.isModelInvalid) { - const maxBuckets = config.get(MAX_BUCKETS_SETTING); - - validateInterval(parsedTimeRange, visParams, maxBuckets); - const untrackSearch = dataSearch.session.isCurrentSession(searchSessionId) && dataSearch.session.trackSearch({ diff --git a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts index dd45812f4ebfc..817812a88ca98 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts @@ -9,7 +9,7 @@ import _ from 'lodash'; import { Framework } from '../plugin'; -import type { TimeseriesVisData } from '../../common/types'; +import type { TimeseriesVisData, FetchedIndexPattern, Series } from '../../common/types'; import { PANEL_TYPES } from '../../common/enums'; import type { VisTypeTimeseriesVisDataRequest, @@ -20,6 +20,8 @@ import { getSeriesData } from './vis_data/get_series_data'; import { getTableData } from './vis_data/get_table_data'; import { getEsQueryConfig } from './vis_data/helpers/get_es_query_uisettings'; import { getCachedIndexPatternFetcher } from './search_strategies/lib/cached_index_pattern_fetcher'; +import { MAX_BUCKETS_SETTING } from '../../common/constants'; +import { getIntervalAndTimefield } from './vis_data/get_interval_and_timefield'; export async function getVisData( requestContext: VisTypeTimeseriesRequestHandlerContext, @@ -32,15 +34,41 @@ export async function getVisData( const esQueryConfig = await getEsQueryConfig(uiSettings); const promises = request.body.panels.map((panel) => { + const cachedIndexPatternFetcher = getCachedIndexPatternFetcher(indexPatternsService, { + fetchKibanaIndexForStringIndexes: Boolean(panel.use_kibana_indexes), + }); const services: VisTypeTimeseriesRequestServices = { esQueryConfig, esShardTimeout, indexPatternsService, uiSettings, + cachedIndexPatternFetcher, searchStrategyRegistry: framework.searchStrategyRegistry, - cachedIndexPatternFetcher: getCachedIndexPatternFetcher(indexPatternsService, { - fetchKibanaIndexForStringIndexes: Boolean(panel.use_kibana_indexes), - }), + buildSeriesMetaParams: async ( + index: FetchedIndexPattern, + useKibanaIndexes: boolean, + series?: Series + ) => { + /** This part of code is required to try to get the default timefield for string indices. + * The rest of the functionality available for Kibana indexes should not be active **/ + if (!useKibanaIndexes && index.indexPatternString) { + index = await cachedIndexPatternFetcher(index.indexPatternString, true); + } + + const maxBuckets = await uiSettings.get(MAX_BUCKETS_SETTING); + const { min, max } = request.body.timerange; + + return getIntervalAndTimefield( + panel, + index, + { + min, + max, + maxBuckets, + }, + series + ); + }, }; return panel.type === PANEL_TYPES.TABLE diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts index 0d1ca9cba022a..62220e08b7e10 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts @@ -11,12 +11,17 @@ import { FetchedIndexPattern, Panel, Series } from '../../../common/types'; describe('getIntervalAndTimefield(panel, series)', () => { const index: FetchedIndexPattern = {} as FetchedIndexPattern; + const params = { + min: '2017-01-01T00:00:00Z', + max: '2017-01-01T01:00:00Z', + maxBuckets: 1000, + }; test('returns the panel interval and timefield', () => { const panel = { time_field: '@timestamp', interval: 'auto' } as Panel; const series = {} as Series; - expect(getIntervalAndTimefield(panel, index, series)).toEqual({ + expect(getIntervalAndTimefield(panel, index, params, series)).toEqual({ timeField: '@timestamp', interval: 'auto', }); @@ -30,7 +35,7 @@ describe('getIntervalAndTimefield(panel, series)', () => { series_time_field: 'time', } as unknown) as Series; - expect(getIntervalAndTimefield(panel, index, series)).toEqual({ + expect(getIntervalAndTimefield(panel, index, params, series)).toEqual({ timeField: 'time', interval: '1m', }); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts index 0e90dfe77e814..b7a22abd825e0 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts @@ -5,13 +5,25 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import moment from 'moment'; import { AUTO_INTERVAL } from '../../../common/constants'; import { validateField } from '../../../common/fields_utils'; +import { validateInterval } from '../../../common/validate_interval'; import type { FetchedIndexPattern, Panel, Series } from '../../../common/types'; -export function getIntervalAndTimefield(panel: Panel, index: FetchedIndexPattern, series?: Series) { +interface IntervalParams { + min: string; + max: string; + maxBuckets: number; +} + +export function getIntervalAndTimefield( + panel: Panel, + index: FetchedIndexPattern, + { min, max, maxBuckets }: IntervalParams, + series?: Series +) { const timeField = (series?.override_index_pattern ? series.series_time_field : panel.time_field) || index.indexPattern?.timeFieldName; @@ -28,6 +40,15 @@ export function getIntervalAndTimefield(panel: Panel, index: FetchedIndexPattern maxBars = series.series_max_bars; } + validateInterval( + { + min: moment.utc(min), + max: moment.utc(max), + }, + interval, + maxBuckets + ); + return { maxBars, timeField, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts index 822331e0ca0d0..8d495d68eb625 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts @@ -8,8 +8,6 @@ import { i18n } from '@kbn/i18n'; -// not typed yet -// @ts-expect-error import { handleErrorResponse } from './handle_error_response'; import { getAnnotations } from './get_annotations'; import { handleResponseBody } from './series/handle_response_body'; @@ -51,6 +49,8 @@ export async function getSeriesData( uiRestrictions: capabilities.uiRestrictions, }; + const handleError = handleErrorResponse(panel); + try { const bodiesPromises = getActiveSeries(panel).map((series) => getSeriesRequestParams(req, panel, panelIndex, series, capabilities, services) @@ -97,14 +97,9 @@ export async function getSeriesData( }, }; } catch (err) { - if (err.body) { - err.response = err.body; - - return { - ...meta, - ...handleErrorResponse(panel)(err), - }; - } - return meta; + return { + ...meta, + ...handleError(err), + }; } } diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts index db2e027f7815c..3f8d30f0ed833 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts @@ -12,20 +12,19 @@ import { get } from 'lodash'; // not typed yet // @ts-expect-error import { buildRequestBody } from './table/build_request_body'; -// @ts-expect-error import { handleErrorResponse } from './handle_error_response'; // @ts-expect-error import { processBucket } from './table/process_bucket'; import { createFieldsFetcher } from '../search_strategies/lib/fields_fetcher'; import { extractFieldLabel } from '../../../common/fields_utils'; + import type { VisTypeTimeseriesRequestHandlerContext, VisTypeTimeseriesRequestServices, VisTypeTimeseriesVisDataRequest, } from '../../types'; import type { Panel } from '../../../common/types'; -import { getIntervalAndTimefield } from './get_interval_and_timefield'; export async function getTableData( requestContext: VisTypeTimeseriesRequestHandlerContext, @@ -67,23 +66,13 @@ export async function getTableData( return panel.pivot_id; }; - const buildSeriesMetaParams = async () => { - let index = panelIndex; - - /** This part of code is required to try to get the default timefield for string indices. - * The rest of the functionality available for Kibana indexes should not be active **/ - if (!panel.use_kibana_indexes && index.indexPatternString) { - index = await services.cachedIndexPatternFetcher(index.indexPatternString, true); - } - - return getIntervalAndTimefield(panel, index); - }; - const meta = { type: panel.type, uiRestrictions: capabilities.uiRestrictions, }; + const handleError = handleErrorResponse(panel); + try { const body = await buildRequestBody( req, @@ -92,7 +81,7 @@ export async function getTableData( panelIndex, capabilities, services.uiSettings, - buildSeriesMetaParams + () => services.buildSeriesMetaParams(panelIndex, Boolean(panel.use_kibana_indexes)) ); const [resp] = await searchStrategy.search(requestContext, req, [ @@ -121,14 +110,9 @@ export async function getTableData( series, }; } catch (err) { - if (err.body) { - err.response = err.body; - - return { - ...meta, - ...handleErrorResponse(panel)(err), - }; - } - return meta; + return { + ...meta, + ...handleError(err), + }; } } diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.js deleted file mode 100644 index b5583ce20d68a..0000000000000 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.js +++ /dev/null @@ -1,37 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export const handleErrorResponse = (panel) => (error) => { - if (error.isBoom && error.status === 401) throw error; - const result = {}; - let errorResponse; - try { - errorResponse = JSON.parse(error.response); - } catch (e) { - errorResponse = error.response; - } - if (!errorResponse && !(error.name === 'KQLSyntaxError')) { - errorResponse = { - message: error.message, - stack: error.stack, - }; - } - if (error.name === 'KQLSyntaxError') { - errorResponse = { - message: error.shortMessage, - stack: error.stack, - }; - } - result[panel.id] = { - id: panel.id, - statusCode: error.statusCode, - error: errorResponse, - series: [], - }; - return result; -}; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.test.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.test.ts new file mode 100644 index 0000000000000..eeb22a3dc32cf --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Panel } from '../../../common/types'; +import { handleErrorResponse, ErrorResponse } from './handle_error_response'; + +describe('handleErrorResponse', () => { + const handleError = handleErrorResponse(({ + id: 'test_panel', + } as unknown) as Panel); + + test('should only handle errors that contain errBody', () => { + expect(handleError(new Error('Test Error'))).toMatchInlineSnapshot(`Object {}`); + + expect(handleError({ errBody: 'test' } as ErrorResponse)).toMatchInlineSnapshot(` + Object { + "test_panel": Object { + "error": "test", + "id": "test_panel", + "series": Array [], + }, + } + `); + }); + + test('should set as error the last value of caused_by', () => { + expect( + handleError({ + errBody: { + error: { + reason: 'wrong 0', + caused_by: { + reason: 'wrong 1', + caused_by: { + caused_by: 'ok', + }, + }, + }, + }, + } as ErrorResponse) + ).toMatchInlineSnapshot(` + Object { + "test_panel": Object { + "error": "ok", + "id": "test_panel", + "series": Array [], + }, + } + `); + }); + + test('should use the previous error message if the actual value is empty', () => { + expect( + handleError({ + errBody: { + error: { + reason: 'ok', + caused_by: { + reason: '', + }, + }, + }, + } as ErrorResponse) + ).toMatchInlineSnapshot(` + Object { + "test_panel": Object { + "error": "ok", + "id": "test_panel", + "series": Array [], + }, + } + `); + }); + + test('shouldn not return empty error message', () => { + expect( + handleError({ + errBody: { + error: { + reason: '', + }, + }, + } as ErrorResponse) + ).toMatchInlineSnapshot(` + Object { + "test_panel": Object { + "error": "Unexpected error", + "id": "test_panel", + "series": Array [], + }, + } + `); + }); +}); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.ts new file mode 100644 index 0000000000000..d9327a0fbb786 --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { i18n } from '@kbn/i18n'; +import type { Panel } from '../../../common/types'; + +type ErrorType = + | { + reason: string; + caused_by?: ErrorType; + } + | string; + +export type ErrorResponse = Error & + Partial<{ + errBody: + | { + error: ErrorType; + } + | string; + }>; + +const getErrorMessage = (errBody: ErrorType, defaultMessage?: string): string | undefined => { + if (typeof errBody === 'string') { + return errBody; + } else { + if (errBody.caused_by) { + return getErrorMessage(errBody.caused_by, errBody.reason); + } + return errBody.reason || defaultMessage; + } +}; + +export const handleErrorResponse = (panel: Panel) => (error: ErrorResponse) => { + const result: Record = {}; + + if (error.errBody) { + const errorResponse = + typeof error.errBody === 'string' ? error.errBody : getErrorMessage(error.errBody.error); + + result[panel.id] = { + id: panel.id, + error: + errorResponse ?? + i18n.translate('visTypeTimeseries.handleErrorResponse.unexpectedError', { + defaultMessage: 'Unexpected error', + }), + series: [], + }; + } + + return result; +}; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js index 08b9801254c2e..022718ece435d 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js @@ -47,7 +47,16 @@ describe('dateHistogram(req, panel, series)', () => { get: async (key) => (key === UI_SETTINGS.HISTOGRAM_MAX_BARS ? 100 : 50), }; buildSeriesMetaParams = jest.fn(async () => { - return getIntervalAndTimefield(panel, indexPattern, series); + return getIntervalAndTimefield( + panel, + indexPattern, + { + min: '2017-01-01T00:00:00Z', + max: '2017-01-01T01:00:00Z', + maxBuckets: 1000, + }, + series + ); }); }); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts index a2248308dc571..aedc4ee7c8a15 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts @@ -7,7 +7,6 @@ */ import { buildRequestBody } from './build_request_body'; -import { getIntervalAndTimefield } from '../get_interval_and_timefield'; import type { FetchedIndexPattern, Panel, Series } from '../../../../common/types'; import type { @@ -27,6 +26,7 @@ export async function getSeriesRequestParams( esShardTimeout, uiSettings, cachedIndexPatternFetcher, + buildSeriesMetaParams, }: VisTypeTimeseriesRequestServices ) { let seriesIndex = panelIndex; @@ -35,18 +35,6 @@ export async function getSeriesRequestParams( seriesIndex = await cachedIndexPatternFetcher(series.series_index_pattern ?? ''); } - const buildSeriesMetaParams = async () => { - let index = seriesIndex; - - /** This part of code is required to try to get the default timefield for string indices. - * The rest of the functionality available for Kibana indexes should not be active **/ - if (!panel.use_kibana_indexes && index.indexPatternString) { - index = await cachedIndexPatternFetcher(index.indexPatternString, true); - } - - return getIntervalAndTimefield(panel, index, series); - }; - const request = await buildRequestBody( req, panel, @@ -55,7 +43,7 @@ export async function getSeriesRequestParams( seriesIndex, capabilities, uiSettings, - buildSeriesMetaParams + () => buildSeriesMetaParams(seriesIndex, Boolean(panel.use_kibana_indexes), series) ); return { diff --git a/src/plugins/vis_type_timeseries/server/types.ts b/src/plugins/vis_type_timeseries/server/types.ts index 2fc46b7cd1f11..a2657f99d222d 100644 --- a/src/plugins/vis_type_timeseries/server/types.ts +++ b/src/plugins/vis_type_timeseries/server/types.ts @@ -6,17 +6,18 @@ * Side Public License, v 1. */ -import { Observable } from 'rxjs'; -import { SharedGlobalConfig } from 'kibana/server'; +import type { Observable } from 'rxjs'; +import type { SharedGlobalConfig } from 'kibana/server'; import type { IRouter, IUiSettingsClient, KibanaRequest } from 'src/core/server'; import type { DataRequestHandlerContext, EsQueryConfig, IndexPatternsService, } from '../../data/server'; -import type { VisPayload } from '../common/types'; +import type { Series, VisPayload } from '../common/types'; import type { SearchStrategyRegistry } from './lib/search_strategies'; import type { CachedIndexPatternFetcher } from './lib/search_strategies/lib/cached_index_pattern_fetcher'; +import type { FetchedIndexPattern } from '../common/types'; export type ConfigObservable = Observable; @@ -35,4 +36,13 @@ export interface VisTypeTimeseriesRequestServices { indexPatternsService: IndexPatternsService; searchStrategyRegistry: SearchStrategyRegistry; cachedIndexPatternFetcher: CachedIndexPatternFetcher; + buildSeriesMetaParams: ( + index: FetchedIndexPattern, + useKibanaIndexes: boolean, + series?: Series + ) => Promise<{ + maxBars: number; + timeField?: string; + interval: string; + }>; } From 21858a570da998ca10dec976788210a3c8d9680c Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 30 Jun 2021 10:06:38 +0200 Subject: [PATCH 3/8] [Lens] Fix value popover spacing (#103081) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../line_curve_option.tsx | 47 +++++++++---------- .../missing_values_option.tsx | 10 +--- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx index ea0a1553ba5e5..1df7744524779 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiSpacer, EuiSwitch } from '@elastic/eui'; +import { EuiFormRow, EuiSwitch } from '@elastic/eui'; import { XYCurveType } from '../types'; export interface LineCurveOptionProps { @@ -28,29 +28,26 @@ export const LineCurveOption: React.FC = ({ isCurveTypeEnabled = true, }) => { return isCurveTypeEnabled ? ( - <> - - { - if (e.target.checked) { - onChange('CURVE_MONOTONE_X'); - } else { - onChange('LINEAR'); - } - }} - data-test-subj="lnsCurveStyleToggle" - /> - - - + + { + if (e.target.checked) { + onChange('CURVE_MONOTONE_X'); + } else { + onChange('LINEAR'); + } + }} + data-test-subj="lnsCurveStyleToggle" + /> + ) : null; }; diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx index a683d4fbf514c..fb6ecec4d2801 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx @@ -7,14 +7,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiButtonGroup, - EuiFormRow, - EuiIconTip, - EuiSuperSelect, - EuiText, - EuiSpacer, -} from '@elastic/eui'; +import { EuiButtonGroup, EuiFormRow, EuiIconTip, EuiSuperSelect, EuiText } from '@elastic/eui'; import { FittingFunction, fittingFunctionDefinitions } from '../fitting_functions'; import { ValueLabelConfig } from '../types'; @@ -140,7 +133,6 @@ export const MissingValuesOptions: React.FC = ({ /> )} - ); }; From 4aca0b7b61b8b5db51f1fc9d0e656f8f02d5ccfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Wed, 30 Jun 2021 10:20:31 +0200 Subject: [PATCH 4/8] =?UTF-8?q?[APM]=20Add=20=E2=80=9CAnalyze=20Data?= =?UTF-8?q?=E2=80=9D=20button=20(#103485)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Shahzad --- .../__snapshots__/apm_telemetry.test.ts.snap | 60 +++++++++++++ x-pack/plugins/apm/common/agent_name.test.ts | 34 ++++++- x-pack/plugins/apm/common/agent_name.ts | 15 +++- .../components/app/correlations/index.tsx | 1 + .../components/app/service_overview/index.tsx | 5 +- .../templates/apm_service_template.tsx | 78 +++++++++++++++- .../shared/agent_icon/get_agent_icon.ts | 5 ++ .../apm/server/lib/apm_telemetry/schema.ts | 3 + .../apm/typings/es_schemas/ui/fields/agent.ts | 2 + .../schema/xpack_plugins.json | 90 +++++++++++++++++++ 10 files changed, 282 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap index 71b0929164705..a2baee6074989 100644 --- a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap @@ -16,6 +16,9 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "dotnet": { "type": "long" }, + "iOS/swift": { + "type": "long" + }, "go": { "type": "long" }, @@ -70,6 +73,9 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "opentelemetry/ruby": { "type": "long" }, + "opentelemetry/swift": { + "type": "long" + }, "opentelemetry/webjs": { "type": "long" } @@ -131,6 +137,60 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, + "iOS/swift": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword" + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword" + }, + "version": { + "type": "keyword" + }, + "composite": { + "type": "keyword" + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword" + }, + "version": { + "type": "keyword" + }, + "composite": { + "type": "keyword" + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword" + }, + "version": { + "type": "keyword" + }, + "composite": { + "type": "keyword" + } + } + } + } + } + } + }, "go": { "properties": { "agent": { diff --git a/x-pack/plugins/apm/common/agent_name.test.ts b/x-pack/plugins/apm/common/agent_name.test.ts index 9f74136efe829..162a5716d6c7b 100644 --- a/x-pack/plugins/apm/common/agent_name.test.ts +++ b/x-pack/plugins/apm/common/agent_name.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isJavaAgentName, isRumAgentName } from './agent_name'; +import { isJavaAgentName, isRumAgentName, isIosAgentName } from './agent_name'; describe('agent name helpers', () => { describe('isJavaAgentName', () => { @@ -22,7 +22,7 @@ describe('agent name helpers', () => { }); describe('when the agent name is not java', () => { - it('returns true', () => { + it('returns false', () => { expect(isJavaAgentName('not java')).toEqual(false); }); }); @@ -47,9 +47,35 @@ describe('agent name helpers', () => { }); }); - describe('when the agent name something else', () => { + describe('when the agent name is something else', () => { + it('returns false', () => { + expect(isRumAgentName('not rum')).toEqual(false); + }); + }); + }); + + describe('isIosAgentName', () => { + describe('when the agent name is js-base', () => { + it('returns true', () => { + expect(isIosAgentName('iOS/swift')).toEqual(true); + }); + }); + + describe('when the agent name is rum-js', () => { it('returns true', () => { - expect(isRumAgentName('java')).toEqual(false); + expect(isIosAgentName('ios/swift')).toEqual(true); + }); + }); + + describe('when the agent name is opentelemetry/swift', () => { + it('returns true', () => { + expect(isIosAgentName('opentelemetry/swift')).toEqual(true); + }); + }); + + describe('when the agent name is something else', () => { + it('returns false', () => { + expect(isIosAgentName('not ios')).toEqual(false); }); }); }); diff --git a/x-pack/plugins/apm/common/agent_name.ts b/x-pack/plugins/apm/common/agent_name.ts index 36bfbabf7797d..650e72751749e 100644 --- a/x-pack/plugins/apm/common/agent_name.ts +++ b/x-pack/plugins/apm/common/agent_name.ts @@ -26,12 +26,14 @@ export const OPEN_TELEMETRY_AGENT_NAMES: AgentName[] = [ 'opentelemetry/php', 'opentelemetry/python', 'opentelemetry/ruby', + 'opentelemetry/swift', 'opentelemetry/webjs', ]; export const AGENT_NAMES: AgentName[] = [ 'dotnet', 'go', + 'iOS/swift', 'java', 'js-base', 'nodejs', @@ -62,7 +64,9 @@ export function isRumAgentName( return RUM_AGENT_NAMES.includes(agentName! as AgentName); } -export function normalizeAgentName(agentName: string | undefined) { +export function normalizeAgentName( + agentName: T +): T | string { if (isRumAgentName(agentName)) { return 'rum-js'; } @@ -71,5 +75,14 @@ export function normalizeAgentName(agentName: string | undefined) { return 'java'; } + if (isIosAgentName(agentName)) { + return 'ios'; + } + return agentName; } + +export function isIosAgentName(agentName?: string) { + const lowercased = agentName && agentName.toLowerCase(); + return lowercased === 'ios/swift' || lowercased === 'opentelemetry/swift'; +} diff --git a/x-pack/plugins/apm/public/components/app/correlations/index.tsx b/x-pack/plugins/apm/public/components/app/correlations/index.tsx index 7b6328916d445..36b298af834ac 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/index.tsx @@ -131,6 +131,7 @@ export function Correlations() { return ( <> { setIsFlyoutVisible(true); }} diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index 5b202e208a52d..fce543b05c6c3 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -8,7 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import React from 'react'; import { useTrackPageview } from '../../../../../observability/public'; -import { isRumAgentName } from '../../../../common/agent_name'; +import { isRumAgentName, isIosAgentName } from '../../../../common/agent_name'; import { AnnotationsContextProvider } from '../../../context/annotations/annotations_context'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context'; @@ -43,6 +43,7 @@ export function ServiceOverview({ serviceName }: ServiceOverviewProps) { const { isMedium } = useBreakPoints(); const rowDirection = isMedium ? 'column' : 'row'; const isRumAgent = isRumAgentName(agentName); + const isIosAgent = isIosAgentName(agentName); return ( @@ -110,7 +111,7 @@ export function ServiceOverview({ serviceName }: ServiceOverviewProps) { )} - {!isRumAgent && ( + {!isRumAgent && !isIosAgent && ( [0] & { key: @@ -54,12 +70,12 @@ interface Props { export function ApmServiceTemplate(props: Props) { return ( -