From fa8a0f8b4b97fffdb47ea7759b142ba506c338bb Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Sat, 6 Jun 2020 15:53:46 +0530 Subject: [PATCH 01/44] Convert time_cache to typescript --- .../{time_cache.js => time_cache.ts} | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) rename src/plugins/vis_type_vega/public/data_model/{time_cache.js => time_cache.ts} (74%) diff --git a/src/plugins/vis_type_vega/public/data_model/time_cache.js b/src/plugins/vis_type_vega/public/data_model/time_cache.ts similarity index 74% rename from src/plugins/vis_type_vega/public/data_model/time_cache.js rename to src/plugins/vis_type_vega/public/data_model/time_cache.ts index cf241655592f3..db95a7d185230 100644 --- a/src/plugins/vis_type_vega/public/data_model/time_cache.js +++ b/src/plugins/vis_type_vega/public/data_model/time_cache.ts @@ -17,26 +17,35 @@ * under the License. */ +import { TimefilterContract, TimeRangeBounds } from '../../../data/public'; +import { TimeRange } from '../../../data/common'; + /** * Optimization caching - always return the same value if queried within this time * @type {number} */ -const AlwaysCacheMaxAge = 40; + +const AlwaysCacheMaxAge: number = 40; /** * This class caches timefilter's bounds to minimize number of server requests */ export class TimeCache { - constructor(timefilter, maxAge) { + _timefilter: TimefilterContract; + _maxAge: number; + _cachedBounds?: TimeRangeBounds; + _cacheTS: number; + _timeRange?: TimeRange; + + constructor(timefilter: TimefilterContract, maxAge: number) { this._timefilter = timefilter; this._maxAge = maxAge; - this._cachedBounds = null; this._cacheTS = 0; } // Simplifies unit testing // noinspection JSMethodCanBeStatic - _now() { + _now(): number { return Date.now(); } @@ -44,10 +53,10 @@ export class TimeCache { * Get cached time range values * @returns {{min: number, max: number}} */ - getTimeBounds() { + getTimeBounds(): TimeRangeBounds { const ts = this._now(); - let bounds; + let bounds: TimeRangeBounds | null = null; if (this._cachedBounds) { const diff = ts - this._cacheTS; @@ -62,8 +71,8 @@ export class TimeCache { if (diff < this._maxAge) { bounds = this._getBounds(); if ( - Math.abs(bounds.min - this._cachedBounds.min) < this._maxAge && - Math.abs(bounds.max - this._cachedBounds.max) < this._maxAge + Math.abs(bounds.min!.valueOf() - this._cachedBounds.min!.valueOf()) < this._maxAge && + Math.abs(bounds.max!.valueOf() - this._cachedBounds.max!.valueOf()) < this._maxAge ) { return this._cachedBounds; } @@ -76,7 +85,7 @@ export class TimeCache { return this._cachedBounds; } - setTimeRange(timeRange) { + setTimeRange(timeRange: TimeRange): void { this._timeRange = timeRange; } @@ -85,11 +94,11 @@ export class TimeCache { * @returns {{min: number, max: number}} * @private */ - _getBounds() { - const bounds = this._timefilter.calculateBounds(this._timeRange); + _getBounds(): TimeRangeBounds { + const bounds = this._timefilter.calculateBounds(this._timeRange!); return { - min: bounds.min.valueOf(), - max: bounds.max.valueOf(), + min: bounds.min, + max: bounds.max, }; } } From a72f06858da57de154722ff3099bce3e3841b92b Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Sat, 6 Jun 2020 15:59:37 +0530 Subject: [PATCH 02/44] Import types --- src/plugins/data/public/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 5540039323756..47b2d088cc758 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -436,6 +436,8 @@ export { TimeHistory, TimefilterContract, TimeHistoryContract, + TimefilterSetup, + TimeRangeBounds, } from './query'; export { From af78f4ddd8c7a68605bff77788ebf839495fef38 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Sat, 6 Jun 2020 17:58:08 +0530 Subject: [PATCH 03/44] Converted search cache to typescript --- src/plugins/data/public/index.ts | 1 + src/plugins/data/public/search/index.ts | 1 + .../{search_cache.js => search_cache.ts} | 15 ++++++++++++--- .../vis_type_vega/public/vega_request_handler.ts | 2 -- 4 files changed, 14 insertions(+), 5 deletions(-) rename src/plugins/vis_type_vega/public/data_model/{search_cache.js => search_cache.ts} (80%) diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 47b2d088cc758..9841e0d4518e3 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -371,6 +371,7 @@ export { TabbedTable, SearchInterceptor, RequestTimeoutError, + LegacyApiCaller, } from './search'; // Search namespace diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 26149432f6117..db1d04bb85958 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -66,3 +66,4 @@ export { export { SearchInterceptor } from './search_interceptor'; export { RequestTimeoutError } from './request_timeout_error'; +export { LegacyApiCaller } from './legacy/es_client'; diff --git a/src/plugins/vis_type_vega/public/data_model/search_cache.js b/src/plugins/vis_type_vega/public/data_model/search_cache.ts similarity index 80% rename from src/plugins/vis_type_vega/public/data_model/search_cache.js rename to src/plugins/vis_type_vega/public/data_model/search_cache.ts index 41e4c67c3b2ad..be77da08dbb55 100644 --- a/src/plugins/vis_type_vega/public/data_model/search_cache.js +++ b/src/plugins/vis_type_vega/public/data_model/search_cache.ts @@ -18,9 +18,18 @@ */ import LruCache from 'lru-cache'; +import { LegacyApiCaller } from '../../../data/public'; + +interface CacheOptions { + max: number; + maxAge: number; +} export class SearchCache { - constructor(es, cacheOpts) { + _es: LegacyApiCaller; + _cache: LruCache>; + + constructor(es: LegacyApiCaller, cacheOpts: CacheOptions) { this._es = es; this._cache = new LruCache(cacheOpts); } @@ -30,8 +39,8 @@ export class SearchCache { * with the new ones already in cache * @param {object[]} requests array of search requests */ - search(requests) { - const promises = []; + search(requests: Array>) { + const promises: Array> = []; for (const request of requests) { const key = JSON.stringify(request); diff --git a/src/plugins/vis_type_vega/public/vega_request_handler.ts b/src/plugins/vis_type_vega/public/vega_request_handler.ts index efc02e368efa8..bee86f9a69f7a 100644 --- a/src/plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/plugins/vis_type_vega/public/vega_request_handler.ts @@ -19,9 +19,7 @@ import { Filter, esQuery, TimeRange, Query } from '../../data/public'; -// @ts-ignore import { SearchCache } from './data_model/search_cache'; -// @ts-ignore import { TimeCache } from './data_model/time_cache'; import { VegaVisualizationDependencies } from './plugin'; From defc41b66aa3c79cf91ebe671563fca9cb0f9af2 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 8 Jun 2020 13:09:37 +0530 Subject: [PATCH 04/44] Url parser abd vega parser to typescript --- .../public/data_model/url_parser.ts | 74 ++ .../public/data_model/vega_parser.ts | 674 ++++++++++++++++++ 2 files changed, 748 insertions(+) create mode 100644 src/plugins/vis_type_vega/public/data_model/url_parser.ts create mode 100644 src/plugins/vis_type_vega/public/data_model/vega_parser.ts diff --git a/src/plugins/vis_type_vega/public/data_model/url_parser.ts b/src/plugins/vis_type_vega/public/data_model/url_parser.ts new file mode 100644 index 0000000000000..b12d55cf375eb --- /dev/null +++ b/src/plugins/vis_type_vega/public/data_model/url_parser.ts @@ -0,0 +1,74 @@ +/* + * 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 $ from 'jquery'; +import { i18n } from '@kbn/i18n'; + +/** + * This class processes all Vega spec customizations, + * converting url object parameters into query results. + */ +export class UrlParser { + _onWarning: (...args: string[]) => void; + constructor(onWarning: (...args: string[]) => void) { + this._onWarning = onWarning; + } + + // noinspection JSMethodCanBeStatic + /** + * Update request object + */ + parseUrl(obj: any, urlObj: any) { + let url = urlObj.url; + if (!url) { + throw new Error( + i18n.translate('visTypeVega.urlParser.dataUrlRequiresUrlParameterInFormErrorMessage', { + defaultMessage: '{dataUrlParam} requires a {urlParam} parameter in a form "{formLink}"', + values: { + dataUrlParam: '"data.url"', + urlParam: '"url"', + formLink: 'https://example.org/path/subpath', + }, + }) + ); + } + + const query = urlObj.query; + if (!query) { + this._onWarning( + i18n.translate('visTypeVega.urlParser.urlShouldHaveQuerySubObjectWarningMessage', { + defaultMessage: 'Using a {urlObject} should have a {subObjectName} sub-object', + values: { + urlObject: '"url": {"%type%": "url", "url": ...}', + subObjectName: '"query"', + }, + }) + ); + } else { + url += (url.includes('?') ? '&' : '?') + $.param(query); + } + + obj.url = url; + } + + /** + * No-op - the url is already set during the parseUrl + */ + populateData() {} +} diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts new file mode 100644 index 0000000000000..d3bcdd3076108 --- /dev/null +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -0,0 +1,674 @@ +/* + * 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 _ from 'lodash'; +import * as vega from 'vega-lib'; +import * as vegaLite from 'vega-lite'; +import schemaParser from 'vega-schema-url-parser'; +import versionCompare from 'compare-versions'; +// @ts-ignore +import hjson from 'hjson'; +import { VISUALIZATION_COLORS } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +// @ts-ignore +import { EsQueryParser } from './es_query_parser'; +// @ts-ignore +import { Utils } from './utils'; +// @ts-ignore +import { EmsFileParser } from './ems_file_parser'; +// @ts-ignore +import { UrlParser } from './url_parser'; +import { SearchCache } from './search_cache'; +import { TimeCache } from './time_cache'; + +// Set default single color to match other Kibana visualizations +const defaultColor: string = VISUALIZATION_COLORS[0]; + +const DEFAULT_SCHEMA: string = 'https://vega.github.io/schema/vega/v3.0.json'; + +const locToDirMap: any = { + left: 'row-reverse', + right: 'row', + top: 'column-reverse', + bottom: 'column', +}; + +// If there is no "%type%" parameter, use this parser +const DEFAULT_PARSER: string = 'elasticsearch'; + +export class VegaParser { + spec: any; + hideWarnings: boolean; + error?: string; + warnings: string[]; + _urlParsers: { [key: string]: any }; + isVegaLite?: boolean; + useHover?: boolean; + _config?: any; + useMap?: boolean; + renderer?: string; + tooltips?: object; + mapConfig?: object; + vlspec?: any; + useResize?: boolean; + paddingWidth?: number; + paddingHeight?: number; + containerDir?: any; + controlsDir?: string; + + constructor( + spec: string, + searchCache: SearchCache, + timeCache: TimeCache, + filters: any, + serviceSettings: any + ) { + this.spec = spec; + this.hideWarnings = false; + + this.error = undefined; + this.warnings = []; + + const onWarn = this._onWarning.bind(this); + this._urlParsers = { + elasticsearch: new EsQueryParser(timeCache, searchCache, filters, onWarn), + emsfile: new EmsFileParser(serviceSettings), + url: new UrlParser(onWarn), + }; + } + + async parseAsync() { + try { + await this._parseAsync(); + } catch (err) { + // if we reject current promise, it will use the standard Kibana error handling + this.error = Utils.formatErrorToStr(err); + } + return this; + } + + async _parseAsync() { + if (this.isVegaLite !== undefined) throw new Error(); + + if (typeof this.spec === 'string') { + this.spec = hjson.parse(this.spec, { legacyRoot: false }); + } + if (!_.isPlainObject(this.spec)) { + throw new Error( + i18n.translate('visTypeVega.vegaParser.invalidVegaSpecErrorMessage', { + defaultMessage: 'Invalid Vega specification', + }) + ); + } + this.isVegaLite = this._parseSchema(); + this.useHover = !this.isVegaLite; + + this._config = this._parseConfig(); + this.hideWarnings = !!this._config.hideWarnings; + this.useMap = this._config.type === 'map'; + this.renderer = this._config.renderer === 'svg' ? 'svg' : 'canvas'; + this.tooltips = this._parseTooltips(); + + this._setDefaultColors(); + this._parseControlPlacement(); + if (this.useMap) { + this.mapConfig = this._parseMapConfig(); + } else if (this.spec.autosize === undefined) { + // Default autosize should be fit, unless it's a map (leaflet-vega handles that) + this.spec.autosize = { type: 'fit', contains: 'padding' }; + } + + await this._resolveDataUrls(); + + if (this.isVegaLite) { + this._compileVegaLite(); + } + + this._calcSizing(); + } + + /** + * Convert VegaLite to Vega spec + * @private + */ + _compileVegaLite() { + this.vlspec = this.spec; + // eslint-disable-next-line import/namespace + const logger = vega.logger(vega.Warn); // note: eslint has a false positive here + logger.warn = this._onWarning.bind(this); + this.spec = vegaLite.compile(this.vlspec, { logger }).spec; + + // When using VL with the type=map and user did not provid their own projection settings, + // remove the default projection that was generated by VegaLite compiler. + // This way we let leaflet-vega library inject a different default projection for tile maps. + // Also, VL injects default padding and autosize values, but neither should be set for vega-leaflet. + if (this.useMap) { + const hasConfig = _.isPlainObject(this.vlspec.config); + if (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.projection)) { + // Assume VL generates spec.projections = an array of exactly one object named 'projection' + if ( + !Array.isArray(this.spec.projections) || + this.spec.projections.length !== 1 || + this.spec.projections[0].name !== 'projection' + ) { + throw new Error( + i18n.translate( + 'visTypeVega.vegaParser.VLCompilerShouldHaveGeneratedSingleProtectionObjectErrorMessage', + { + defaultMessage: + 'Internal error: Vega-Lite compiler should have generated a single projection object', + } + ) + ); + } + delete this.spec.projections; + } + + // todo: sizing cleanup might need to be rethought and consolidated + if (!this.vlspec.width) delete this.spec.width; + if (!this.vlspec.height) delete this.spec.height; + if ( + !this.vlspec.padding && + (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.padding)) + ) { + delete this.spec.padding; + } + if ( + !this.vlspec.autosize && + (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.autosize)) + ) { + delete this.spec.autosize; + } + } + } + + /** + * Process graph size and padding + * @private + */ + _calcSizing() { + this.useResize = false; + if (!this.useMap) { + // when useResize is true, vega's canvas size will be set based on the size of the container, + // and will be automatically updated on resize events. + // We delete width & height if the autosize is set to "fit" + // We also set useResize=true in case autosize=none, and width & height are not set + const autosize = this.spec.autosize.type || this.spec.autosize; + if (autosize === 'fit' || (autosize === 'none' && !this.spec.width && !this.spec.height)) { + this.useResize = true; + } + } + + // Padding is not included in the width/height by default + this.paddingWidth = 0; + this.paddingHeight = 0; + if (this.useResize && this.spec.padding && this.spec.autosize.contains !== 'padding') { + if (typeof this.spec.padding === 'object') { + this.paddingWidth += (+this.spec.padding.left || 0) + (+this.spec.padding.right || 0); + this.paddingHeight += (+this.spec.padding.top || 0) + (+this.spec.padding.bottom || 0); + } else { + this.paddingWidth += 2 * (+this.spec.padding || 0); + this.paddingHeight += 2 * (+this.spec.padding || 0); + } + } + + if (this.useResize && (this.spec.width || this.spec.height)) { + if (this.isVegaLite) { + delete this.spec.width; + delete this.spec.height; + } else { + this._onWarning( + i18n.translate( + 'visTypeVega.vegaParser.widthAndHeightParamsAreIgnoredWithAutosizeFitWarningMessage', + { + defaultMessage: + 'The {widthParam} and {heightParam} params are ignored with {autosizeParam}', + values: { + autosizeParam: 'autosize=fit', + widthParam: '"width"', + heightParam: '"height"', + }, + } + ) + ); + } + } + } + + /** + * Calculate container-direction CSS property for binding placement + * @private + */ + _parseControlPlacement() { + this.containerDir = this._config?.controlsLocation + ? locToDirMap[this._config.controlsLocation] + : undefined; + if (this.containerDir === undefined) { + if (this._config && this._config.controlsLocation === undefined) { + this.containerDir = 'column'; + } else { + throw new Error( + i18n.translate('visTypeVega.vegaParser.unrecognizedControlsLocationValueErrorMessage', { + defaultMessage: + 'Unrecognized {controlsLocationParam} value. Expecting one of [{locToDirMap}]', + values: { + locToDirMap: `"${locToDirMap.keys().join('", "')}"`, + controlsLocationParam: 'controlsLocation', + }, + }) + ); + } + } + const dir = this._config?.controlsDirection; + if (dir !== undefined && dir !== 'horizontal' && dir !== 'vertical') { + throw new Error( + i18n.translate('visTypeVega.vegaParser.unrecognizedDirValueErrorMessage', { + defaultMessage: 'Unrecognized {dirParam} value. Expecting one of [{expectedValues}]', + values: { expectedValues: '"horizontal", "vertical"', dirParam: 'dir' }, + }) + ); + } + this.controlsDir = dir === 'horizontal' ? 'row' : 'column'; + } + + /** + * Parse {config: kibana: {...}} portion of the Vega spec (or root-level _hostConfig for backward compat) + * @returns {object} kibana config + * @private + */ + _parseConfig(): object { + let result; + if (this.spec._hostConfig !== undefined) { + result = this.spec._hostConfig; + delete this.spec._hostConfig; + if (!_.isPlainObject(result)) { + throw new Error( + i18n.translate('visTypeVega.vegaParser.hostConfigValueTypeErrorMessage', { + defaultMessage: 'If present, {configName} must be an object', + values: { configName: '"_hostConfig"' }, + }) + ); + } + this._onWarning( + i18n.translate('visTypeVega.vegaParser.hostConfigIsDeprecatedWarningMessage', { + defaultMessage: + '{deprecatedConfigName} has been deprecated. Use {newConfigName} instead.', + values: { + deprecatedConfigName: '"_hostConfig"', + newConfigName: 'config.kibana', + }, + }) + ); + } + if (_.isPlainObject(this.spec.config) && this.spec.config.kibana !== undefined) { + result = this.spec.config.kibana; + delete this.spec.config.kibana; + if (!_.isPlainObject(result)) { + throw new Error( + i18n.translate('visTypeVega.vegaParser.kibanaConfigValueTypeErrorMessage', { + defaultMessage: 'If present, {configName} must be an object', + values: { configName: 'config.kibana' }, + }) + ); + } + } + return result || {}; + } + + _parseTooltips() { + if (this._config && this._config.tooltips === false) { + return false; + } + + const result = this._config.tooltips || {}; + + if (result.position === undefined) { + result.position = 'top'; + } else if (['top', 'right', 'bottom', 'left'].indexOf(result.position) === -1) { + throw new Error( + i18n.translate( + 'visTypeVega.vegaParser.unexpectedValueForPositionConfigurationErrorMessage', + { + defaultMessage: 'Unexpected value for the {configurationName} configuration', + values: { configurationName: 'result.position' }, + } + ) + ); + } + + if (result.padding === undefined) { + result.padding = 16; + } else if (typeof result.padding !== 'number') { + throw new Error( + i18n.translate('visTypeVega.vegaParser.paddingConfigValueTypeErrorMessage', { + defaultMessage: '{configName} is expected to be a number', + values: { configName: 'config.kibana.result.padding' }, + }) + ); + } + + if (result.centerOnMark === undefined) { + // if mark's width & height is less than this value, center on it + result.centerOnMark = 50; + } else if (typeof result.centerOnMark === 'boolean') { + result.centerOnMark = result.centerOnMark ? Number.MAX_VALUE : -1; + } else if (typeof result.centerOnMark !== 'number') { + throw new Error( + i18n.translate('visTypeVega.vegaParser.centerOnMarkConfigValueTypeErrorMessage', { + defaultMessage: '{configName} is expected to be {trueValue}, {falseValue}, or a number', + values: { + configName: 'config.kibana.result.centerOnMark', + trueValue: 'true', + falseValue: 'false', + }, + }) + ); + } + + return result; + } + + /** + * Parse map-specific configuration + * @returns {{mapStyle: *|string, delayRepaint: boolean, latitude: number, longitude: number, zoom, minZoom, maxZoom, zoomControl: *|boolean, maxBounds: *}} + * @private + */ + _parseMapConfig() { + const res: { [key: string]: any } = { + delayRepaint: this._config.delayRepaint === undefined ? true : this._config.delayRepaint, + }; + + const validate = (name: string, isZoom: boolean) => { + const val = this._config[name]; + if (val !== undefined) { + const parsed = parseFloat(val); + if (Number.isFinite(parsed) && (!isZoom || (parsed >= 0 && parsed <= 30))) { + res[name] = parsed; + return; + } + this._onWarning( + i18n.translate('visTypeVega.vegaParser.someKibanaConfigurationIsNoValidWarningMessage', { + defaultMessage: '{configName} is not valid', + values: { configName: `config.kibana.${name}` }, + }) + ); + } + if (!isZoom) res[name] = 0; + }; + + validate(`latitude`, false); + validate(`longitude`, false); + validate(`zoom`, true); + validate(`minZoom`, true); + validate(`maxZoom`, true); + + // `false` is a valid value + res.mapStyle = this._config.mapStyle === undefined ? `default` : this._config.mapStyle; + if (res.mapStyle !== `default` && res.mapStyle !== false) { + this._onWarning( + i18n.translate('visTypeVega.vegaParser.mapStyleValueTypeWarningMessage', { + defaultMessage: + '{mapStyleConfigName} may either be {mapStyleConfigFirstAllowedValue} or {mapStyleConfigSecondAllowedValue}', + values: { + mapStyleConfigName: 'config.kibana.mapStyle', + mapStyleConfigFirstAllowedValue: 'false', + mapStyleConfigSecondAllowedValue: '"default"', + }, + }) + ); + res.mapStyle = `default`; + } + + this._parseBool('zoomControl', res, true); + this._parseBool('scrollWheelZoom', res, false); + + const maxBounds = this._config.maxBounds; + if (maxBounds !== undefined) { + if ( + !Array.isArray(maxBounds) || + maxBounds.length !== 4 || + !maxBounds.every((v) => typeof v === 'number' && Number.isFinite(v)) + ) { + this._onWarning( + i18n.translate('visTypeVega.vegaParser.maxBoundsValueTypeWarningMessage', { + defaultMessage: '{maxBoundsConfigName} must be an array with four numbers', + values: { + maxBoundsConfigName: 'config.kibana.maxBounds', + }, + }) + ); + } else { + res.maxBounds = maxBounds; + } + } + + return res; + } + + _parseBool(paramName: string, dstObj: any, dflt: any) { + const val = this._config[paramName]; + if (val === undefined) { + dstObj[paramName] = dflt; + } else if (typeof val !== 'boolean') { + this._onWarning( + i18n.translate('visTypeVega.vegaParser.someKibanaParamValueTypeWarningMessage', { + defaultMessage: '{configName} must be a boolean value', + values: { + configName: `config.kibana.${paramName}`, + }, + }) + ); + dstObj[paramName] = dflt; + } else { + dstObj[paramName] = val; + } + } + + /** + * Parse Vega schema element + * @returns {boolean} is this a VegaLite schema? + * @private + */ + _parseSchema() { + if (!this.spec.$schema) { + this._onWarning( + i18n.translate('visTypeVega.vegaParser.inputSpecDoesNotSpecifySchemaWarningMessage', { + defaultMessage: + 'The input spec does not specify a {schemaParam}, defaulting to {defaultSchema}', + values: { defaultSchema: `"${DEFAULT_SCHEMA}"`, schemaParam: '"$schema"' }, + }) + ); + this.spec.$schema = DEFAULT_SCHEMA; + } + + const schema = schemaParser(this.spec.$schema); + const isVegaLite = schema.library === 'vega-lite'; + const libVersion = isVegaLite ? vegaLite.version : vega.version; + + if (versionCompare(schema.version, libVersion) > 0) { + this._onWarning( + i18n.translate('visTypeVega.vegaParser.notValidLibraryVersionForInputSpecWarningMessage', { + defaultMessage: + 'The input spec uses {schemaLibrary} {schemaVersion}, but current version of {schemaLibrary} is {libraryVersion}.', + values: { + schemaLibrary: schema.library, + schemaVersion: schema.version, + libraryVersion: libVersion, + }, + }) + ); + } + + return isVegaLite; + } + + /** + * Replace all instances of ES requests with raw values. + * Also handle any other type of url: {type: xxx, ...} + * @private + */ + async _resolveDataUrls() { + const pending: { [key: string]: any } = {}; + + this._findObjectDataUrls(this.spec, (obj: any) => { + const url = obj.url; + delete obj.url; + let type: string = url['%type%']; + delete url['%type%']; + if (type === undefined) { + type = DEFAULT_PARSER; + } + + const parser = this._urlParsers[type]; + if (parser === undefined) { + throw new Error( + i18n.translate('visTypeVega.vegaParser.notSupportedUrlTypeErrorMessage', { + defaultMessage: '{urlObject} is not supported', + values: { + urlObject: 'url: {"%type%": "${type}"}', + }, + }) + ); + } + + let pendingArr = pending[type]; + if (pendingArr === undefined) { + pending[type] = pendingArr = []; + } + + pendingArr.push(parser.parseUrl(obj, url)); + }); + + const pendingParsers = Object.keys(pending); + if (pendingParsers.length > 0) { + // let each parser populate its data in parallel + await Promise.all( + pendingParsers.map((type) => this._urlParsers[type].populateData(pending[type])) + ); + } + } + + /** + * Recursively find and callback every instance of the data.url as an object + * @param {*} obj current location in the object tree + * @param {function({object})} onFind Call this function for all url objects + * @param {string} [key] field name of the current object + * @private + */ + _findObjectDataUrls(obj: any, onFind: any, key?: any) { + if (Array.isArray(obj)) { + for (const elem of obj) { + this._findObjectDataUrls(elem, onFind, key); + } + } else if (_.isPlainObject(obj)) { + if (key === 'data' && _.isPlainObject(obj.url)) { + // Assume that any "data": {"url": {...}} is a request for data + if (obj.values !== undefined || obj.source !== undefined) { + throw new Error( + i18n.translate( + 'visTypeVega.vegaParser.dataExceedsSomeParamsUseTimesLimitErrorMessage', + { + defaultMessage: + 'Data must not have more than one of {urlParam}, {valuesParam}, and {sourceParam}', + values: { + urlParam: '"url"', + valuesParam: '"values"', + sourceParam: '"source"', + }, + } + ) + ); + } + onFind(obj); + } else { + for (const k of Object.keys(obj)) { + this._findObjectDataUrls(obj[k], onFind, k); + } + } + } + } + + /** + * Inject default colors into the spec.config + * @private + */ + _setDefaultColors() { + // Default category coloring to the Elastic color scheme + this._setDefaultValue({ scheme: 'elastic' }, 'config', 'range', 'category'); + + if (this.isVegaLite) { + // Vega-Lite: set default color, works for fill and strike -- config: { mark: { color: '#54B399' }} + this._setDefaultValue(defaultColor, 'config', 'mark', 'color'); + } else { + // Vega - global mark has very strange behavior, must customize each mark type individually + // https://github.com/vega/vega/issues/1083 + // Don't set defaults if spec.config.mark.color or fill are set + if ( + !this.spec.config.mark || + (this.spec.config.mark.color === undefined && this.spec.config.mark.fill === undefined) + ) { + this._setDefaultValue(defaultColor, 'config', 'arc', 'fill'); + this._setDefaultValue(defaultColor, 'config', 'area', 'fill'); + this._setDefaultValue(defaultColor, 'config', 'line', 'stroke'); + this._setDefaultValue(defaultColor, 'config', 'path', 'stroke'); + this._setDefaultValue(defaultColor, 'config', 'rect', 'fill'); + this._setDefaultValue(defaultColor, 'config', 'rule', 'stroke'); + this._setDefaultValue(defaultColor, 'config', 'shape', 'stroke'); + this._setDefaultValue(defaultColor, 'config', 'symbol', 'fill'); + this._setDefaultValue(defaultColor, 'config', 'trail', 'fill'); + } + } + } + + /** + * Set default value if it doesn't exist. + * Given an object, and an array of fields, ensure that obj.fld1.fld2. ... .fldN is set to value if it doesn't exist. + * @param {*} value + * @param {string} fields + * @private + */ + _setDefaultValue(value: unknown, ...fields: string[]) { + let o = this.spec; + for (let i = 0; i < fields.length - 1; i++) { + const field = fields[i]; + const subObj = o[field]; + if (subObj === undefined) { + o[field] = {}; + } else if (!_.isPlainObject(subObj)) { + return; + } + o = o[field]; + } + const lastField = fields[fields.length - 1]; + if (o[lastField] === undefined) { + o[lastField] = value; + } + } + + /** + * Add a warning to the warnings array + * @private + */ + _onWarning(...args: string[]) { + if (!this.hideWarnings) { + this.warnings.push(Utils.formatWarningToStr(args)); + return Utils.formatWarningToStr(args); + } + } +} From b00121493ae97a6d974795db6427b83faedd1b07 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Wed, 10 Jun 2020 22:30:41 +0530 Subject: [PATCH 05/44] Converted es query parser to typescript --- ...{es_query_parser.js => es_query_parser.ts} | 49 +- .../public/data_model/time_cache.ts | 2 +- .../public/data_model/url_parser.js | 73 -- .../public/data_model/vega_parser.js | 638 ------------------ .../public/data_model/vega_parser.ts | 1 - 5 files changed, 33 insertions(+), 730 deletions(-) rename src/plugins/vis_type_vega/public/data_model/{es_query_parser.js => es_query_parser.ts} (91%) delete mode 100644 src/plugins/vis_type_vega/public/data_model/url_parser.js delete mode 100644 src/plugins/vis_type_vega/public/data_model/vega_parser.js diff --git a/src/plugins/vis_type_vega/public/data_model/es_query_parser.js b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts similarity index 91% rename from src/plugins/vis_type_vega/public/data_model/es_query_parser.js rename to src/plugins/vis_type_vega/public/data_model/es_query_parser.ts index 066c9f06fc109..428b66b261312 100644 --- a/src/plugins/vis_type_vega/public/data_model/es_query_parser.js +++ b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts @@ -22,22 +22,35 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { getEsShardTimeout } from '../services'; +import { TimeCache } from './time_cache'; +import { SearchCache } from './search_cache'; -const TIMEFILTER = '%timefilter%'; -const AUTOINTERVAL = '%autointerval%'; -const MUST_CLAUSE = '%dashboard_context-must_clause%'; -const MUST_NOT_CLAUSE = '%dashboard_context-must_not_clause%'; +const TIMEFILTER: string = '%timefilter%'; +const AUTOINTERVAL: string = '%autointerval%'; +const MUST_CLAUSE: string = '%dashboard_context-must_clause%'; +const MUST_NOT_CLAUSE: string = '%dashboard_context-must_not_clause%'; // These values may appear in the 'url': { ... } object -const LEGACY_CONTEXT = '%context_query%'; -const CONTEXT = '%context%'; -const TIMEFIELD = '%timefield%'; +const LEGACY_CONTEXT: string = '%context_query%'; +const CONTEXT: string = '%context%'; +const TIMEFIELD: string = '%timefield%'; /** * This class parses ES requests specified in the data.url objects. */ export class EsQueryParser { - constructor(timeCache, searchCache, filters, onWarning) { + _timeCache: TimeCache; + _searchCache: SearchCache; + _filters: any; + _onWarning: (...args: string[]) => void; + _esShardTimeout: number; + + constructor( + timeCache: TimeCache, + searchCache: SearchCache, + filters: any, + onWarning: (...args: string[]) => void + ) { this._timeCache = timeCache; this._searchCache = searchCache; this._filters = filters; @@ -49,7 +62,7 @@ export class EsQueryParser { /** * Update request object, expanding any context-aware keywords */ - parseUrl(dataObject, url) { + parseUrl(dataObject: any, url: any) { let body = url.body; let context = url[CONTEXT]; delete url[CONTEXT]; @@ -179,11 +192,11 @@ export class EsQueryParser { return { dataObject, url }; } - mapRequest = (request) => { + mapRequest = (request: any) => { const esRequest = request.url; if (this._esShardTimeout) { // remove possible timeout query param to prevent two conflicting timeout parameters - const { body = {}, timeout, ...rest } = esRequest; //eslint-disable-line no-unused-vars + const { body = {}, timeout, ...rest } = esRequest; // eslint-disable-line no-unused-vars body.timeout = `${this._esShardTimeout}ms`; return { body, @@ -199,7 +212,7 @@ export class EsQueryParser { * @param {object[]} requests each object is generated by parseUrl() * @returns {Promise} */ - async populateData(requests) { + async populateData(requests: any) { const esSearches = requests.map(this.mapRequest); const results = await this._searchCache.search(esSearches); @@ -214,7 +227,7 @@ export class EsQueryParser { * @param {*} obj * @param {boolean} isQuery - if true, the `obj` belongs to the req's query portion */ - _injectContextVars(obj, isQuery) { + _injectContextVars(obj: any, isQuery: any) { if (obj && typeof obj === 'object') { if (Array.isArray(obj)) { // For arrays, replace MUST_CLAUSE and MUST_NOT_CLAUSE string elements @@ -262,7 +275,9 @@ export class EsQueryParser { ); } const bounds = this._timeCache.getTimeBounds(); - obj.interval = EsQueryParser._roundInterval((bounds.max - bounds.min) / size); + obj.interval = EsQueryParser._roundInterval( + (bounds.max.valueOf() - bounds.min.valueOf()) / size + ); continue; } @@ -304,7 +319,7 @@ export class EsQueryParser { * @param {object} obj * @return {object} */ - _createRangeFilter(obj) { + _createRangeFilter(obj: any) { obj.gte = moment(this._getTimeBound(obj, 'min')).toISOString(); obj.lte = moment(this._getTimeBound(obj, 'max')).toISOString(); obj.format = 'strict_date_optional_time'; @@ -322,7 +337,7 @@ export class EsQueryParser { * @param {'min'|'max'} type * @returns {*} */ - _getTimeBound(opts, type) { + _getTimeBound(opts: any, type: any) { const bounds = this._timeCache.getTimeBounds(); let result = bounds[type]; @@ -382,7 +397,7 @@ export class EsQueryParser { * @param interval (ms) * @returns {string} */ - static _roundInterval(interval) { + static _roundInterval(interval: number): string { switch (true) { case interval <= 500: // <= 0.5s return '100ms'; diff --git a/src/plugins/vis_type_vega/public/data_model/time_cache.ts b/src/plugins/vis_type_vega/public/data_model/time_cache.ts index db95a7d185230..912e2920de133 100644 --- a/src/plugins/vis_type_vega/public/data_model/time_cache.ts +++ b/src/plugins/vis_type_vega/public/data_model/time_cache.ts @@ -53,7 +53,7 @@ export class TimeCache { * Get cached time range values * @returns {{min: number, max: number}} */ - getTimeBounds(): TimeRangeBounds { + getTimeBounds(): any { const ts = this._now(); let bounds: TimeRangeBounds | null = null; diff --git a/src/plugins/vis_type_vega/public/data_model/url_parser.js b/src/plugins/vis_type_vega/public/data_model/url_parser.js deleted file mode 100644 index 9a30f12e08232..0000000000000 --- a/src/plugins/vis_type_vega/public/data_model/url_parser.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import $ from 'jquery'; -import { i18n } from '@kbn/i18n'; - -/** - * This class processes all Vega spec customizations, - * converting url object parameters into query results. - */ -export class UrlParser { - constructor(onWarning) { - this._onWarning = onWarning; - } - - // noinspection JSMethodCanBeStatic - /** - * Update request object - */ - parseUrl(obj, urlObj) { - let url = urlObj.url; - if (!url) { - throw new Error( - i18n.translate('visTypeVega.urlParser.dataUrlRequiresUrlParameterInFormErrorMessage', { - defaultMessage: '{dataUrlParam} requires a {urlParam} parameter in a form "{formLink}"', - values: { - dataUrlParam: '"data.url"', - urlParam: '"url"', - formLink: 'https://example.org/path/subpath', - }, - }) - ); - } - - const query = urlObj.query; - if (!query) { - this._onWarning( - i18n.translate('visTypeVega.urlParser.urlShouldHaveQuerySubObjectWarningMessage', { - defaultMessage: 'Using a {urlObject} should have a {subObjectName} sub-object', - values: { - urlObject: '"url": {"%type%": "url", "url": ...}', - subObjectName: '"query"', - }, - }) - ); - } else { - url += (url.includes('?') ? '&' : '?') + $.param(query); - } - - obj.url = url; - } - - /** - * No-op - the url is already set during the parseUrl - */ - populateData() {} -} diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.js b/src/plugins/vis_type_vega/public/data_model/vega_parser.js deleted file mode 100644 index f541b9f104adc..0000000000000 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.js +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import * as vega from 'vega-lib'; -import * as vegaLite from 'vega-lite'; -import schemaParser from 'vega-schema-url-parser'; -import versionCompare from 'compare-versions'; -import { EsQueryParser } from './es_query_parser'; -import hjson from 'hjson'; -import { Utils } from './utils'; -import { EmsFileParser } from './ems_file_parser'; -import { UrlParser } from './url_parser'; -import { VISUALIZATION_COLORS } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -// Set default single color to match other Kibana visualizations -const defaultColor = VISUALIZATION_COLORS[0]; - -const DEFAULT_SCHEMA = 'https://vega.github.io/schema/vega/v3.0.json'; - -const locToDirMap = { - left: 'row-reverse', - right: 'row', - top: 'column-reverse', - bottom: 'column', -}; - -// If there is no "%type%" parameter, use this parser -const DEFAULT_PARSER = 'elasticsearch'; - -export class VegaParser { - constructor(spec, searchCache, timeCache, filters, serviceSettings) { - this.spec = spec; - this.hideWarnings = false; - this.error = undefined; - this.warnings = []; - - const onWarn = this._onWarning.bind(this); - this._urlParsers = { - elasticsearch: new EsQueryParser(timeCache, searchCache, filters, onWarn), - emsfile: new EmsFileParser(serviceSettings), - url: new UrlParser(onWarn), - }; - } - - async parseAsync() { - try { - await this._parseAsync(); - } catch (err) { - // if we reject current promise, it will use the standard Kibana error handling - this.error = Utils.formatErrorToStr(err); - } - return this; - } - - async _parseAsync() { - if (this.isVegaLite !== undefined) throw new Error(); - - if (typeof this.spec === 'string') { - this.spec = hjson.parse(this.spec, { legacyRoot: false }); - } - if (!_.isPlainObject(this.spec)) { - throw new Error( - i18n.translate('visTypeVega.vegaParser.invalidVegaSpecErrorMessage', { - defaultMessage: 'Invalid Vega specification', - }) - ); - } - this.isVegaLite = this._parseSchema(); - this.useHover = !this.isVegaLite; - - this._config = this._parseConfig(); - this.hideWarnings = !!this._config.hideWarnings; - this.useMap = this._config.type === 'map'; - this.renderer = this._config.renderer === 'svg' ? 'svg' : 'canvas'; - this.tooltips = this._parseTooltips(); - - this._setDefaultColors(); - this._parseControlPlacement(this._config); - if (this.useMap) { - this.mapConfig = this._parseMapConfig(); - } else if (this.spec.autosize === undefined) { - // Default autosize should be fit, unless it's a map (leaflet-vega handles that) - this.spec.autosize = { type: 'fit', contains: 'padding' }; - } - - await this._resolveDataUrls(); - - if (this.isVegaLite) { - this._compileVegaLite(); - } - - this._calcSizing(); - } - - /** - * Convert VegaLite to Vega spec - * @private - */ - _compileVegaLite() { - this.vlspec = this.spec; - // eslint-disable-next-line import/namespace - const logger = vega.logger(vega.Warn); // note: eslint has a false positive here - logger.warn = this._onWarning.bind(this); - this.spec = vegaLite.compile(this.vlspec, logger).spec; - - // When using VL with the type=map and user did not provid their own projection settings, - // remove the default projection that was generated by VegaLite compiler. - // This way we let leaflet-vega library inject a different default projection for tile maps. - // Also, VL injects default padding and autosize values, but neither should be set for vega-leaflet. - if (this.useMap) { - const hasConfig = _.isPlainObject(this.vlspec.config); - if (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.projection)) { - // Assume VL generates spec.projections = an array of exactly one object named 'projection' - if ( - !Array.isArray(this.spec.projections) || - this.spec.projections.length !== 1 || - this.spec.projections[0].name !== 'projection' - ) { - throw new Error( - i18n.translate( - 'visTypeVega.vegaParser.VLCompilerShouldHaveGeneratedSingleProtectionObjectErrorMessage', - { - defaultMessage: - 'Internal error: Vega-Lite compiler should have generated a single projection object', - } - ) - ); - } - delete this.spec.projections; - } - - // todo: sizing cleanup might need to be rethought and consolidated - if (!this.vlspec.width) delete this.spec.width; - if (!this.vlspec.height) delete this.spec.height; - if ( - !this.vlspec.padding && - (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.padding)) - ) { - delete this.spec.padding; - } - if ( - !this.vlspec.autosize && - (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.autosize)) - ) { - delete this.spec.autosize; - } - } - } - - /** - * Process graph size and padding - * @private - */ - _calcSizing() { - this.useResize = false; - if (!this.useMap) { - // when useResize is true, vega's canvas size will be set based on the size of the container, - // and will be automatically updated on resize events. - // We delete width & height if the autosize is set to "fit" - // We also set useResize=true in case autosize=none, and width & height are not set - const autosize = this.spec.autosize.type || this.spec.autosize; - if (autosize === 'fit' || (autosize === 'none' && !this.spec.width && !this.spec.height)) { - this.useResize = true; - } - } - - // Padding is not included in the width/height by default - this.paddingWidth = 0; - this.paddingHeight = 0; - if (this.useResize && this.spec.padding && this.spec.autosize.contains !== 'padding') { - if (typeof this.spec.padding === 'object') { - this.paddingWidth += (+this.spec.padding.left || 0) + (+this.spec.padding.right || 0); - this.paddingHeight += (+this.spec.padding.top || 0) + (+this.spec.padding.bottom || 0); - } else { - this.paddingWidth += 2 * (+this.spec.padding || 0); - this.paddingHeight += 2 * (+this.spec.padding || 0); - } - } - - if (this.useResize && (this.spec.width || this.spec.height)) { - if (this.isVegaLite) { - delete this.spec.width; - delete this.spec.height; - } else { - this._onWarning( - i18n.translate( - 'visTypeVega.vegaParser.widthAndHeightParamsAreIgnoredWithAutosizeFitWarningMessage', - { - defaultMessage: - 'The {widthParam} and {heightParam} params are ignored with {autosizeParam}', - values: { - autosizeParam: 'autosize=fit', - widthParam: '"width"', - heightParam: '"height"', - }, - } - ) - ); - } - } - } - - /** - * Calculate container-direction CSS property for binding placement - * @private - */ - _parseControlPlacement() { - this.containerDir = locToDirMap[this._config.controlsLocation]; - if (this.containerDir === undefined) { - if (this._config.controlsLocation === undefined) { - this.containerDir = 'column'; - } else { - throw new Error( - i18n.translate('visTypeVega.vegaParser.unrecognizedControlsLocationValueErrorMessage', { - defaultMessage: - 'Unrecognized {controlsLocationParam} value. Expecting one of [{locToDirMap}]', - values: { - locToDirMap: `"${locToDirMap.keys().join('", "')}"`, - controlsLocationParam: 'controlsLocation', - }, - }) - ); - } - } - const dir = this._config.controlsDirection; - if (dir !== undefined && dir !== 'horizontal' && dir !== 'vertical') { - throw new Error( - i18n.translate('visTypeVega.vegaParser.unrecognizedDirValueErrorMessage', { - defaultMessage: 'Unrecognized {dirParam} value. Expecting one of [{expectedValues}]', - values: { expectedValues: '"horizontal", "vertical"', dirParam: 'dir' }, - }) - ); - } - this.controlsDir = dir === 'horizontal' ? 'row' : 'column'; - } - - /** - * Parse {config: kibana: {...}} portion of the Vega spec (or root-level _hostConfig for backward compat) - * @returns {object} kibana config - * @private - */ - _parseConfig() { - let result; - if (this.spec._hostConfig !== undefined) { - result = this.spec._hostConfig; - delete this.spec._hostConfig; - if (!_.isPlainObject(result)) { - throw new Error( - i18n.translate('visTypeVega.vegaParser.hostConfigValueTypeErrorMessage', { - defaultMessage: 'If present, {configName} must be an object', - values: { configName: '"_hostConfig"' }, - }) - ); - } - this._onWarning( - i18n.translate('visTypeVega.vegaParser.hostConfigIsDeprecatedWarningMessage', { - defaultMessage: - '{deprecatedConfigName} has been deprecated. Use {newConfigName} instead.', - values: { - deprecatedConfigName: '"_hostConfig"', - newConfigName: 'config.kibana', - }, - }) - ); - } - if (_.isPlainObject(this.spec.config) && this.spec.config.kibana !== undefined) { - result = this.spec.config.kibana; - delete this.spec.config.kibana; - if (!_.isPlainObject(result)) { - throw new Error( - i18n.translate('visTypeVega.vegaParser.kibanaConfigValueTypeErrorMessage', { - defaultMessage: 'If present, {configName} must be an object', - values: { configName: 'config.kibana' }, - }) - ); - } - } - return result || {}; - } - - _parseTooltips() { - if (this._config.tooltips === false) { - return false; - } - - const result = this._config.tooltips || {}; - - if (result.position === undefined) { - result.position = 'top'; - } else if (['top', 'right', 'bottom', 'left'].indexOf(result.position) === -1) { - throw new Error( - i18n.translate( - 'visTypeVega.vegaParser.unexpectedValueForPositionConfigurationErrorMessage', - { - defaultMessage: 'Unexpected value for the {configurationName} configuration', - values: { configurationName: 'result.position' }, - } - ) - ); - } - - if (result.padding === undefined) { - result.padding = 16; - } else if (typeof result.padding !== 'number') { - throw new Error( - i18n.translate('visTypeVega.vegaParser.paddingConfigValueTypeErrorMessage', { - defaultMessage: '{configName} is expected to be a number', - values: { configName: 'config.kibana.result.padding' }, - }) - ); - } - - if (result.centerOnMark === undefined) { - // if mark's width & height is less than this value, center on it - result.centerOnMark = 50; - } else if (typeof result.centerOnMark === 'boolean') { - result.centerOnMark = result.centerOnMark ? Number.MAX_VALUE : -1; - } else if (typeof result.centerOnMark !== 'number') { - throw new Error( - i18n.translate('visTypeVega.vegaParser.centerOnMarkConfigValueTypeErrorMessage', { - defaultMessage: '{configName} is expected to be {trueValue}, {falseValue}, or a number', - values: { - configName: 'config.kibana.result.centerOnMark', - trueValue: 'true', - falseValue: 'false', - }, - }) - ); - } - - return result; - } - - /** - * Parse map-specific configuration - * @returns {{mapStyle: *|string, delayRepaint: boolean, latitude: number, longitude: number, zoom, minZoom, maxZoom, zoomControl: *|boolean, maxBounds: *}} - * @private - */ - _parseMapConfig() { - const res = { - delayRepaint: this._config.delayRepaint === undefined ? true : this._config.delayRepaint, - }; - - const validate = (name, isZoom) => { - const val = this._config[name]; - if (val !== undefined) { - const parsed = parseFloat(val); - if (Number.isFinite(parsed) && (!isZoom || (parsed >= 0 && parsed <= 30))) { - res[name] = parsed; - return; - } - this._onWarning( - i18n.translate('visTypeVega.vegaParser.someKibanaConfigurationIsNoValidWarningMessage', { - defaultMessage: '{configName} is not valid', - values: { configName: `config.kibana.${name}` }, - }) - ); - } - if (!isZoom) res[name] = 0; - }; - - validate(`latitude`, false); - validate(`longitude`, false); - validate(`zoom`, true); - validate(`minZoom`, true); - validate(`maxZoom`, true); - - // `false` is a valid value - res.mapStyle = this._config.mapStyle === undefined ? `default` : this._config.mapStyle; - if (res.mapStyle !== `default` && res.mapStyle !== false) { - this._onWarning( - i18n.translate('visTypeVega.vegaParser.mapStyleValueTypeWarningMessage', { - defaultMessage: - '{mapStyleConfigName} may either be {mapStyleConfigFirstAllowedValue} or {mapStyleConfigSecondAllowedValue}', - values: { - mapStyleConfigName: 'config.kibana.mapStyle', - mapStyleConfigFirstAllowedValue: 'false', - mapStyleConfigSecondAllowedValue: '"default"', - }, - }) - ); - res.mapStyle = `default`; - } - - this._parseBool('zoomControl', res, true); - this._parseBool('scrollWheelZoom', res, false); - - const maxBounds = this._config.maxBounds; - if (maxBounds !== undefined) { - if ( - !Array.isArray(maxBounds) || - maxBounds.length !== 4 || - !maxBounds.every((v) => typeof v === 'number' && Number.isFinite(v)) - ) { - this._onWarning( - i18n.translate('visTypeVega.vegaParser.maxBoundsValueTypeWarningMessage', { - defaultMessage: '{maxBoundsConfigName} must be an array with four numbers', - values: { - maxBoundsConfigName: 'config.kibana.maxBounds', - }, - }) - ); - } else { - res.maxBounds = maxBounds; - } - } - - return res; - } - - _parseBool(paramName, dstObj, dflt) { - const val = this._config[paramName]; - if (val === undefined) { - dstObj[paramName] = dflt; - } else if (typeof val !== 'boolean') { - this._onWarning( - i18n.translate('visTypeVega.vegaParser.someKibanaParamValueTypeWarningMessage', { - defaultMessage: '{configName} must be a boolean value', - values: { - configName: `config.kibana.${paramName}`, - }, - }) - ); - dstObj[paramName] = dflt; - } else { - dstObj[paramName] = val; - } - } - - /** - * Parse Vega schema element - * @returns {boolean} is this a VegaLite schema? - * @private - */ - _parseSchema() { - if (!this.spec.$schema) { - this._onWarning( - i18n.translate('visTypeVega.vegaParser.inputSpecDoesNotSpecifySchemaWarningMessage', { - defaultMessage: - 'The input spec does not specify a {schemaParam}, defaulting to {defaultSchema}', - values: { defaultSchema: `"${DEFAULT_SCHEMA}"`, schemaParam: '"$schema"' }, - }) - ); - this.spec.$schema = DEFAULT_SCHEMA; - } - - const schema = schemaParser(this.spec.$schema); - const isVegaLite = schema.library === 'vega-lite'; - const libVersion = isVegaLite ? vegaLite.version : vega.version; - - if (versionCompare(schema.version, libVersion) > 0) { - this._onWarning( - i18n.translate('visTypeVega.vegaParser.notValidLibraryVersionForInputSpecWarningMessage', { - defaultMessage: - 'The input spec uses {schemaLibrary} {schemaVersion}, but current version of {schemaLibrary} is {libraryVersion}.', - values: { - schemaLibrary: schema.library, - schemaVersion: schema.version, - libraryVersion: libVersion, - }, - }) - ); - } - - return isVegaLite; - } - - /** - * Replace all instances of ES requests with raw values. - * Also handle any other type of url: {type: xxx, ...} - * @private - */ - async _resolveDataUrls() { - const pending = {}; - - this._findObjectDataUrls(this.spec, (obj) => { - const url = obj.url; - delete obj.url; - let type = url['%type%']; - delete url['%type%']; - if (type === undefined) { - type = DEFAULT_PARSER; - } - - const parser = this._urlParsers[type]; - if (parser === undefined) { - throw new Error( - i18n.translate('visTypeVega.vegaParser.notSupportedUrlTypeErrorMessage', { - defaultMessage: '{urlObject} is not supported', - values: { - urlObject: 'url: {"%type%": "${type}"}', - }, - }) - ); - } - - let pendingArr = pending[type]; - if (pendingArr === undefined) { - pending[type] = pendingArr = []; - } - - pendingArr.push(parser.parseUrl(obj, url)); - }); - - const pendingParsers = Object.keys(pending); - if (pendingParsers.length > 0) { - // let each parser populate its data in parallel - await Promise.all( - pendingParsers.map((type) => this._urlParsers[type].populateData(pending[type])) - ); - } - } - - /** - * Recursively find and callback every instance of the data.url as an object - * @param {*} obj current location in the object tree - * @param {function({object})} onFind Call this function for all url objects - * @param {string} [key] field name of the current object - * @private - */ - _findObjectDataUrls(obj, onFind, key) { - if (Array.isArray(obj)) { - for (const elem of obj) { - this._findObjectDataUrls(elem, onFind, key); - } - } else if (_.isPlainObject(obj)) { - if (key === 'data' && _.isPlainObject(obj.url)) { - // Assume that any "data": {"url": {...}} is a request for data - if (obj.values !== undefined || obj.source !== undefined) { - throw new Error( - i18n.translate( - 'visTypeVega.vegaParser.dataExceedsSomeParamsUseTimesLimitErrorMessage', - { - defaultMessage: - 'Data must not have more than one of {urlParam}, {valuesParam}, and {sourceParam}', - values: { - urlParam: '"url"', - valuesParam: '"values"', - sourceParam: '"source"', - }, - } - ) - ); - } - onFind(obj); - } else { - for (const k of Object.keys(obj)) { - this._findObjectDataUrls(obj[k], onFind, k); - } - } - } - } - - /** - * Inject default colors into the spec.config - * @private - */ - _setDefaultColors() { - // Default category coloring to the Elastic color scheme - this._setDefaultValue({ scheme: 'elastic' }, 'config', 'range', 'category'); - - if (this.isVegaLite) { - // Vega-Lite: set default color, works for fill and strike -- config: { mark: { color: '#54B399' }} - this._setDefaultValue(defaultColor, 'config', 'mark', 'color'); - } else { - // Vega - global mark has very strange behavior, must customize each mark type individually - // https://github.com/vega/vega/issues/1083 - // Don't set defaults if spec.config.mark.color or fill are set - if ( - !this.spec.config.mark || - (this.spec.config.mark.color === undefined && this.spec.config.mark.fill === undefined) - ) { - this._setDefaultValue(defaultColor, 'config', 'arc', 'fill'); - this._setDefaultValue(defaultColor, 'config', 'area', 'fill'); - this._setDefaultValue(defaultColor, 'config', 'line', 'stroke'); - this._setDefaultValue(defaultColor, 'config', 'path', 'stroke'); - this._setDefaultValue(defaultColor, 'config', 'rect', 'fill'); - this._setDefaultValue(defaultColor, 'config', 'rule', 'stroke'); - this._setDefaultValue(defaultColor, 'config', 'shape', 'stroke'); - this._setDefaultValue(defaultColor, 'config', 'symbol', 'fill'); - this._setDefaultValue(defaultColor, 'config', 'trail', 'fill'); - } - } - } - - /** - * Set default value if it doesn't exist. - * Given an object, and an array of fields, ensure that obj.fld1.fld2. ... .fldN is set to value if it doesn't exist. - * @param {*} value - * @param {string} fields - * @private - */ - _setDefaultValue(value, ...fields) { - let o = this.spec; - for (let i = 0; i < fields.length - 1; i++) { - const field = fields[i]; - const subObj = o[field]; - if (subObj === undefined) { - o[field] = {}; - } else if (!_.isPlainObject(subObj)) { - return; - } - o = o[field]; - } - const lastField = fields[fields.length - 1]; - if (o[lastField] === undefined) { - o[lastField] = value; - } - } - - /** - * Add a warning to the warnings array - * @private - */ - _onWarning() { - if (!this.hideWarnings) { - this.warnings.push(Utils.formatWarningToStr(...arguments)); - } - } -} diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index d3bcdd3076108..7bbb4b5e2b4db 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -26,7 +26,6 @@ import versionCompare from 'compare-versions'; import hjson from 'hjson'; import { VISUALIZATION_COLORS } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -// @ts-ignore import { EsQueryParser } from './es_query_parser'; // @ts-ignore import { Utils } from './utils'; From 73f65c27b369e47eefb3b587c2fba57255eb728f Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 11 Jun 2020 00:52:40 +0530 Subject: [PATCH 06/44] Converted utils to typescript --- .../public/data_model/{utils.js => utils.ts} | 23 +++++++++++-------- .../public/data_model/vega_parser.ts | 3 +-- 2 files changed, 14 insertions(+), 12 deletions(-) rename src/plugins/vis_type_vega/public/data_model/{utils.js => utils.ts} (70%) diff --git a/src/plugins/vis_type_vega/public/data_model/utils.js b/src/plugins/vis_type_vega/public/data_model/utils.ts similarity index 70% rename from src/plugins/vis_type_vega/public/data_model/utils.js rename to src/plugins/vis_type_vega/public/data_model/utils.ts index 9cf5e36b81294..3f03310a7156b 100644 --- a/src/plugins/vis_type_vega/public/data_model/utils.js +++ b/src/plugins/vis_type_vega/public/data_model/utils.ts @@ -23,13 +23,14 @@ export class Utils { /** * If the 2nd array parameter in args exists, append it to the warning/error string value */ - static formatWarningToStr(value) { - if (arguments.length >= 2) { + static formatWarningToStr(...args: any[]) { + let value = args[0]; + if (args.length >= 2) { try { - if (typeof arguments[1] === 'string') { - value += `\n${arguments[1]}`; + if (typeof args[1] === 'string') { + value += `\n${args[1]}`; } else { - value += '\n' + compactStringify(arguments[1], { maxLength: 70 }); + value += '\n' + compactStringify(args[1], { maxLength: 70 }); } } catch (err) { // ignore @@ -38,12 +39,14 @@ export class Utils { return value; } - static formatErrorToStr(error) { - if (!error) { + static formatErrorToStr(...args: any[]) { + const err: Error = args[0]; + let error: string; + if (!err) { error = 'ERR'; - } else if (error instanceof Error) { - error = error.message; + } else { + error = err.message; } - return Utils.formatWarningToStr(error, ...Array.from(arguments).slice(1)); + return Utils.formatWarningToStr(error, ...Array.from(args).slice(1)); } } diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 7bbb4b5e2b4db..f4b5a141f3bd5 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -27,7 +27,6 @@ import hjson from 'hjson'; import { VISUALIZATION_COLORS } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EsQueryParser } from './es_query_parser'; -// @ts-ignore import { Utils } from './utils'; // @ts-ignore import { EmsFileParser } from './ems_file_parser'; @@ -664,7 +663,7 @@ export class VegaParser { * Add a warning to the warnings array * @private */ - _onWarning(...args: string[]) { + _onWarning(...args: any[]) { if (!this.hideWarnings) { this.warnings.push(Utils.formatWarningToStr(args)); return Utils.formatWarningToStr(args); From 2b129ecb5afcf9699fb5e310b5a160745b0c94b7 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 11 Jun 2020 21:14:03 +0530 Subject: [PATCH 07/44] Vega parser to typescript --- .../public/data_model/vega_parser.ts | 105 ++++++++++-------- 1 file changed, 61 insertions(+), 44 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index f4b5a141f3bd5..12402b147194b 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -34,6 +34,8 @@ import { EmsFileParser } from './ems_file_parser'; import { UrlParser } from './url_parser'; import { SearchCache } from './search_cache'; import { TimeCache } from './time_cache'; +import { IServiceSettings } from '../../../maps_legacy/public'; +import { Bool, Data, VegaSpec } from './types'; // Set default single color to match other Kibana visualizations const defaultColor: string = VISUALIZATION_COLORS[0]; @@ -51,7 +53,7 @@ const locToDirMap: any = { const DEFAULT_PARSER: string = 'elasticsearch'; export class VegaParser { - spec: any; + spec: VegaSpec | string; hideWarnings: boolean; error?: string; warnings: string[]; @@ -74,8 +76,8 @@ export class VegaParser { spec: string, searchCache: SearchCache, timeCache: TimeCache, - filters: any, - serviceSettings: any + filters: Bool, + serviceSettings: IServiceSettings ) { this.spec = spec; this.hideWarnings = false; @@ -127,9 +129,9 @@ export class VegaParser { this._parseControlPlacement(); if (this.useMap) { this.mapConfig = this._parseMapConfig(); - } else if (this.spec.autosize === undefined) { + } else if ((this.spec as VegaSpec).autosize === undefined) { // Default autosize should be fit, unless it's a map (leaflet-vega handles that) - this.spec.autosize = { type: 'fit', contains: 'padding' }; + (this.spec as VegaSpec).autosize = { type: 'fit', contains: 'padding' }; } await this._resolveDataUrls(); @@ -161,9 +163,9 @@ export class VegaParser { if (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.projection)) { // Assume VL generates spec.projections = an array of exactly one object named 'projection' if ( - !Array.isArray(this.spec.projections) || - this.spec.projections.length !== 1 || - this.spec.projections[0].name !== 'projection' + !Array.isArray((this.spec as VegaSpec).projections) || + (this.spec as VegaSpec).projections.length !== 1 || + (this.spec as VegaSpec).projections[0].name !== 'projection' ) { throw new Error( i18n.translate( @@ -175,23 +177,23 @@ export class VegaParser { ) ); } - delete this.spec.projections; + delete (this.spec as VegaSpec).projections; } // todo: sizing cleanup might need to be rethought and consolidated - if (!this.vlspec.width) delete this.spec.width; - if (!this.vlspec.height) delete this.spec.height; + if (!this.vlspec.width) delete (this.spec as VegaSpec).width; + if (!this.vlspec.height) delete (this.spec as VegaSpec).height; if ( !this.vlspec.padding && (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.padding)) ) { - delete this.spec.padding; + delete (this.spec as VegaSpec).padding; } if ( !this.vlspec.autosize && (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.autosize)) ) { - delete this.spec.autosize; + delete (this.spec as VegaSpec).autosize; } } } @@ -207,8 +209,11 @@ export class VegaParser { // and will be automatically updated on resize events. // We delete width & height if the autosize is set to "fit" // We also set useResize=true in case autosize=none, and width & height are not set - const autosize = this.spec.autosize.type || this.spec.autosize; - if (autosize === 'fit' || (autosize === 'none' && !this.spec.width && !this.spec.height)) { + const autosize = (this.spec as VegaSpec).autosize.type || (this.spec as VegaSpec).autosize; + if ( + autosize === 'fit' || + (autosize === 'none' && !(this.spec as VegaSpec).width && !(this.spec as VegaSpec).height) + ) { this.useResize = true; } } @@ -216,20 +221,28 @@ export class VegaParser { // Padding is not included in the width/height by default this.paddingWidth = 0; this.paddingHeight = 0; - if (this.useResize && this.spec.padding && this.spec.autosize.contains !== 'padding') { - if (typeof this.spec.padding === 'object') { - this.paddingWidth += (+this.spec.padding.left || 0) + (+this.spec.padding.right || 0); - this.paddingHeight += (+this.spec.padding.top || 0) + (+this.spec.padding.bottom || 0); + if ( + this.useResize && + (this.spec as VegaSpec).padding && + (this.spec as VegaSpec).autosize.contains !== 'padding' + ) { + if (typeof (this.spec as VegaSpec).padding === 'object') { + this.paddingWidth += + (+(this.spec as VegaSpec).padding.left || 0) + + (+(this.spec as VegaSpec).padding.right || 0); + this.paddingHeight += + (+(this.spec as VegaSpec).padding.top || 0) + + (+(this.spec as VegaSpec).padding.bottom || 0); } else { - this.paddingWidth += 2 * (+this.spec.padding || 0); - this.paddingHeight += 2 * (+this.spec.padding || 0); + this.paddingWidth += 2 * (+(this.spec as VegaSpec).padding || 0); + this.paddingHeight += 2 * (+(this.spec as VegaSpec).padding || 0); } } - if (this.useResize && (this.spec.width || this.spec.height)) { + if (this.useResize && ((this.spec as VegaSpec).width || (this.spec as VegaSpec).height)) { if (this.isVegaLite) { - delete this.spec.width; - delete this.spec.height; + delete (this.spec as VegaSpec).width; + delete (this.spec as VegaSpec).height; } else { this._onWarning( i18n.translate( @@ -292,9 +305,9 @@ export class VegaParser { */ _parseConfig(): object { let result; - if (this.spec._hostConfig !== undefined) { - result = this.spec._hostConfig; - delete this.spec._hostConfig; + if ((this.spec as VegaSpec)._hostConfig !== undefined) { + result = (this.spec as VegaSpec)._hostConfig; + delete (this.spec as VegaSpec)._hostConfig; if (!_.isPlainObject(result)) { throw new Error( i18n.translate('visTypeVega.vegaParser.hostConfigValueTypeErrorMessage', { @@ -314,9 +327,12 @@ export class VegaParser { }) ); } - if (_.isPlainObject(this.spec.config) && this.spec.config.kibana !== undefined) { - result = this.spec.config.kibana; - delete this.spec.config.kibana; + if ( + _.isPlainObject((this.spec as VegaSpec).config) && + (this.spec as VegaSpec).config.kibana !== undefined + ) { + result = (this.spec as VegaSpec).config.kibana; + delete (this.spec as VegaSpec).config.kibana; if (!_.isPlainObject(result)) { throw new Error( i18n.translate('visTypeVega.vegaParser.kibanaConfigValueTypeErrorMessage', { @@ -484,7 +500,7 @@ export class VegaParser { * @private */ _parseSchema() { - if (!this.spec.$schema) { + if (!(this.spec as VegaSpec).$schema) { this._onWarning( i18n.translate('visTypeVega.vegaParser.inputSpecDoesNotSpecifySchemaWarningMessage', { defaultMessage: @@ -492,10 +508,10 @@ export class VegaParser { values: { defaultSchema: `"${DEFAULT_SCHEMA}"`, schemaParam: '"$schema"' }, }) ); - this.spec.$schema = DEFAULT_SCHEMA; + (this.spec as VegaSpec).$schema = DEFAULT_SCHEMA; } - const schema = schemaParser(this.spec.$schema); + const schema = schemaParser((this.spec as VegaSpec).$schema); const isVegaLite = schema.library === 'vega-lite'; const libVersion = isVegaLite ? vegaLite.version : vega.version; @@ -524,11 +540,11 @@ export class VegaParser { async _resolveDataUrls() { const pending: { [key: string]: any } = {}; - this._findObjectDataUrls(this.spec, (obj: any) => { + this._findObjectDataUrls(this.spec, (obj: Data) => { const url = obj.url; delete obj.url; - let type: string = url['%type%']; - delete url['%type%']; + let type = url!['%type%']; + delete url!['%type%']; if (type === undefined) { type = DEFAULT_PARSER; } @@ -569,7 +585,7 @@ export class VegaParser { * @param {string} [key] field name of the current object * @private */ - _findObjectDataUrls(obj: any, onFind: any, key?: any) { + _findObjectDataUrls(obj: any, onFind: (data: Data) => void, key?: any) { if (Array.isArray(obj)) { for (const elem of obj) { this._findObjectDataUrls(elem, onFind, key); @@ -618,8 +634,9 @@ export class VegaParser { // https://github.com/vega/vega/issues/1083 // Don't set defaults if spec.config.mark.color or fill are set if ( - !this.spec.config.mark || - (this.spec.config.mark.color === undefined && this.spec.config.mark.fill === undefined) + !(this.spec as VegaSpec).config.mark || + ((this.spec as VegaSpec).config.mark.color === undefined && + (this.spec as VegaSpec).config.mark.fill === undefined) ) { this._setDefaultValue(defaultColor, 'config', 'arc', 'fill'); this._setDefaultValue(defaultColor, 'config', 'area', 'fill'); @@ -645,17 +662,17 @@ export class VegaParser { let o = this.spec; for (let i = 0; i < fields.length - 1; i++) { const field = fields[i]; - const subObj = o[field]; + const subObj = (o as VegaSpec)[field]; if (subObj === undefined) { - o[field] = {}; + (o as VegaSpec)[field] = {}; } else if (!_.isPlainObject(subObj)) { return; } - o = o[field]; + o = (o as VegaSpec)[field]; } const lastField = fields[fields.length - 1]; - if (o[lastField] === undefined) { - o[lastField] = value; + if ((o as VegaSpec)[lastField] === undefined) { + (o as VegaSpec)[lastField] = value; } } From 480d047f76fd7b5c420a48f26c6aad3742f77110 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 11 Jun 2020 21:15:44 +0530 Subject: [PATCH 08/44] Added types --- .../vis_type_vega/public/data_model/types.ts | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/plugins/vis_type_vega/public/data_model/types.ts diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts new file mode 100644 index 0000000000000..d4a5d1bc71659 --- /dev/null +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -0,0 +1,151 @@ +/* + * 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 { AggParamsMapping, Filter } from 'src/plugins/data/public'; +import { DslQuery } from 'src/plugins/data/common'; + +interface Coordinate { + axis: { + title: string; + }; + field: string; +} + +interface Encoding { + x: Coordinate; + y: Coordinate; +} + +interface AutoSize { + type: string; + contains: string; +} + +export interface VegaSpec { + [index: string]: any; + $schema: string; + data?: Data; + encoding?: Encoding; + mark?: string; + title?: string; + autosize: AutoSize; + projections?: any; + width?: any; + height?: any; + padding?: any; + _hostConfig?: any; + config?: any; +} + +export enum CONSTANTS { + TIMEFILTER = '%timefilter%', + CONTEXT = '%context%', + LEGACY_CONTEXT = '%context_query%', + TYPE = '%type%', + SYMBOL = 'Symbol(vega_id)', +} + +export interface Opts { + [index: string]: any; + [CONSTANTS.TIMEFILTER]?: boolean; + gte?: string; + lte?: string; + format?: string; + shift?: number; + unit?: string; +} + +export type Type = 'min' | 'max'; + +export interface TimeBucket { + key_as_string: string; + key: number; + doc_count: number; + [CONSTANTS.SYMBOL]: number; +} + +export interface Bool { + bool?: Bool; + must: DslQuery[]; + filter: Filter[]; + should: never[]; + must_not: Filter[]; +} + +export interface Query { + range?: { [x: number]: Opts }; + bool?: Bool; +} + +export interface UrlObject { + [index: string]: any; + [CONSTANTS.TIMEFILTER]?: string; + [CONSTANTS.CONTEXT]?: boolean; + [CONSTANTS.LEGACY_CONTEXT]?: string; + [CONSTANTS.TYPE]?: string; + index?: string; + body?: { + aggs?: AggParamsMapping; + query?: Query; + timeout?: string; + }; + size?: number; + timeout?: string; +} + +export interface Data { + format?: { + help?: string; + property?: string; + }; + values?: + | { + aggregations?: { + time_buckets: TimeBucket[]; + }; + } + | string; + hits?: { + hits?: any[]; + max_score?: number; + total?: { + relation: string; + value: number; + }; + timed_out?: boolean; + took?: number; + }; + _shards?: { + failed?: number; + skipped?: number; + successful?: number; + total?: number; + }; + url?: UrlObject; +} + +export interface Requests { + dataObject?: Data; + url: UrlObject; +} + +export interface CacheOptions { + max: number; + maxAge: number; +} From c2669e2202b33a2893939a9da47dd4338ce21fe0 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 11 Jun 2020 21:19:33 +0530 Subject: [PATCH 09/44] Converted to typescript --- src/plugins/data/public/index.ts | 1 + src/plugins/data/public/search/index.ts | 1 + .../public/map/service_settings.d.ts | 1 + ...{ems_file_parser.js => ems_file_parser.ts} | 14 +++++++--- .../public/data_model/es_query_parser.ts | 27 ++++++++++--------- .../public/data_model/search_cache.ts | 8 ++---- .../public/data_model/time_cache.ts | 2 +- .../public/data_model/vega_parser.ts | 2 -- 8 files changed, 30 insertions(+), 26 deletions(-) rename src/plugins/vis_type_vega/public/data_model/{ems_file_parser.js => ems_file_parser.ts} (87%) diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 9841e0d4518e3..cb74663ac6a1e 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -372,6 +372,7 @@ export { SearchInterceptor, RequestTimeoutError, LegacyApiCaller, + AggParamsMapping, } from './search'; // Search namespace diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index db1d04bb85958..bc5a2d2847adc 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -27,6 +27,7 @@ export { ISearchContext, TSearchStrategyProvider, ISearchStrategy, + AggsParamsMapping, } from './types'; export { diff --git a/src/plugins/maps_legacy/public/map/service_settings.d.ts b/src/plugins/maps_legacy/public/map/service_settings.d.ts index e265accaeb8fd..105836ff25f8b 100644 --- a/src/plugins/maps_legacy/public/map/service_settings.d.ts +++ b/src/plugins/maps_legacy/public/map/service_settings.d.ts @@ -48,4 +48,5 @@ export interface IServiceSettings { getEMSHotLink(layer: FileLayer): Promise; getTMSServices(): Promise; getFileLayers(): Promise; + getUrlForRegionLayer(layer: FileLayer): Promise; } diff --git a/src/plugins/vis_type_vega/public/data_model/ems_file_parser.js b/src/plugins/vis_type_vega/public/data_model/ems_file_parser.ts similarity index 87% rename from src/plugins/vis_type_vega/public/data_model/ems_file_parser.js rename to src/plugins/vis_type_vega/public/data_model/ems_file_parser.ts index ecdf6a43d5287..abea4d4eb70ec 100644 --- a/src/plugins/vis_type_vega/public/data_model/ems_file_parser.js +++ b/src/plugins/vis_type_vega/public/data_model/ems_file_parser.ts @@ -18,14 +18,20 @@ */ import { i18n } from '@kbn/i18n'; +// @ts-ignore import { bypassExternalUrlCheck } from '../vega_view/vega_base_view'; +import { IServiceSettings, FileLayer } from '../../../maps_legacy/public'; +import { Data } from './types'; /** * This class processes all Vega spec customizations, * converting url object parameters into query results. */ export class EmsFileParser { - constructor(serviceSettings) { + _serviceSettings: IServiceSettings; + _fileLayersP?: Promise; + + constructor(serviceSettings: IServiceSettings) { this._serviceSettings = serviceSettings; } @@ -33,7 +39,7 @@ export class EmsFileParser { /** * Update request object, expanding any context-aware keywords */ - parseUrl(obj, url) { + parseUrl(obj: Data, url: any) { if (typeof url.name !== 'string') { throw new Error( i18n.translate('visTypeVega.emsFileParser.missingNameOfFileErrorMessage', { @@ -59,13 +65,13 @@ export class EmsFileParser { * @param {object[]} requests each object is generated by parseUrl() * @returns {Promise} */ - async populateData(requests) { + async populateData(requests: any) { if (requests.length === 0) return; const layers = await this._fileLayersP; for (const { obj, name } of requests) { - const foundLayer = layers.find((v) => v.name === name); + const foundLayer = layers?.find((v) => v.name === name); if (!foundLayer) { throw new Error( i18n.translate('visTypeVega.emsFileParser.emsFileNameDoesNotExistErrorMessage', { diff --git a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts index 428b66b261312..467e463ef3957 100644 --- a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts @@ -24,6 +24,7 @@ import { i18n } from '@kbn/i18n'; import { getEsShardTimeout } from '../services'; import { TimeCache } from './time_cache'; import { SearchCache } from './search_cache'; +import { Opts, Type, Data, UrlObject, Requests, Bool } from './types'; const TIMEFILTER: string = '%timefilter%'; const AUTOINTERVAL: string = '%autointerval%'; @@ -41,14 +42,14 @@ const TIMEFIELD: string = '%timefield%'; export class EsQueryParser { _timeCache: TimeCache; _searchCache: SearchCache; - _filters: any; + _filters: Bool; _onWarning: (...args: string[]) => void; _esShardTimeout: number; constructor( timeCache: TimeCache, searchCache: SearchCache, - filters: any, + filters: Bool, onWarning: (...args: string[]) => void ) { this._timeCache = timeCache; @@ -62,7 +63,7 @@ export class EsQueryParser { /** * Update request object, expanding any context-aware keywords */ - parseUrl(dataObject: any, url: any) { + parseUrl(dataObject: Data, url: UrlObject) { let body = url.body; let context = url[CONTEXT]; delete url[CONTEXT]; @@ -182,17 +183,17 @@ export class EsQueryParser { // Use dashboard context const newQuery = _.cloneDeep(this._filters); if (timefield) { - newQuery.bool.must.push(body.query); + newQuery.bool!.must.push(body.query); } body.query = newQuery; } } - this._injectContextVars(body.aggs, false); + this._injectContextVars(body.aggs!, false); return { dataObject, url }; } - mapRequest = (request: any) => { + mapRequest = (request: Requests) => { const esRequest = request.url; if (this._esShardTimeout) { // remove possible timeout query param to prevent two conflicting timeout parameters @@ -212,13 +213,13 @@ export class EsQueryParser { * @param {object[]} requests each object is generated by parseUrl() * @returns {Promise} */ - async populateData(requests: any) { + async populateData(requests: Requests[]) { const esSearches = requests.map(this.mapRequest); const results = await this._searchCache.search(esSearches); for (let i = 0; i < requests.length; i++) { - requests[i].dataObject.values = results[i]; + requests[i].dataObject!.values = results[i]; } } @@ -227,7 +228,7 @@ export class EsQueryParser { * @param {*} obj * @param {boolean} isQuery - if true, the `obj` belongs to the req's query portion */ - _injectContextVars(obj: any, isQuery: any) { + _injectContextVars(obj: any, isQuery: boolean) { if (obj && typeof obj === 'object') { if (Array.isArray(obj)) { // For arrays, replace MUST_CLAUSE and MUST_NOT_CLAUSE string elements @@ -276,7 +277,7 @@ export class EsQueryParser { } const bounds = this._timeCache.getTimeBounds(); obj.interval = EsQueryParser._roundInterval( - (bounds.max.valueOf() - bounds.min.valueOf()) / size + (bounds.max!.valueOf() - bounds.min!.valueOf()) / size ); continue; } @@ -319,7 +320,7 @@ export class EsQueryParser { * @param {object} obj * @return {object} */ - _createRangeFilter(obj: any) { + _createRangeFilter(obj: Opts) { obj.gte = moment(this._getTimeBound(obj, 'min')).toISOString(); obj.lte = moment(this._getTimeBound(obj, 'max')).toISOString(); obj.format = 'strict_date_optional_time'; @@ -337,9 +338,9 @@ export class EsQueryParser { * @param {'min'|'max'} type * @returns {*} */ - _getTimeBound(opts: any, type: any) { + _getTimeBound(opts: Opts, type: Type): number { const bounds = this._timeCache.getTimeBounds(); - let result = bounds[type]; + let result = bounds[type]?.valueOf() || 0; if (opts.shift) { const shift = opts.shift; diff --git a/src/plugins/vis_type_vega/public/data_model/search_cache.ts b/src/plugins/vis_type_vega/public/data_model/search_cache.ts index be77da08dbb55..1287104826c05 100644 --- a/src/plugins/vis_type_vega/public/data_model/search_cache.ts +++ b/src/plugins/vis_type_vega/public/data_model/search_cache.ts @@ -19,11 +19,7 @@ import LruCache from 'lru-cache'; import { LegacyApiCaller } from '../../../data/public'; - -interface CacheOptions { - max: number; - maxAge: number; -} +import { UrlObject, CacheOptions } from './types'; export class SearchCache { _es: LegacyApiCaller; @@ -39,7 +35,7 @@ export class SearchCache { * with the new ones already in cache * @param {object[]} requests array of search requests */ - search(requests: Array>) { + search(requests: UrlObject[]) { const promises: Array> = []; for (const request of requests) { diff --git a/src/plugins/vis_type_vega/public/data_model/time_cache.ts b/src/plugins/vis_type_vega/public/data_model/time_cache.ts index 912e2920de133..db95a7d185230 100644 --- a/src/plugins/vis_type_vega/public/data_model/time_cache.ts +++ b/src/plugins/vis_type_vega/public/data_model/time_cache.ts @@ -53,7 +53,7 @@ export class TimeCache { * Get cached time range values * @returns {{min: number, max: number}} */ - getTimeBounds(): any { + getTimeBounds(): TimeRangeBounds { const ts = this._now(); let bounds: TimeRangeBounds | null = null; diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 12402b147194b..e87aef532cd5e 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -28,9 +28,7 @@ import { VISUALIZATION_COLORS } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EsQueryParser } from './es_query_parser'; import { Utils } from './utils'; -// @ts-ignore import { EmsFileParser } from './ems_file_parser'; -// @ts-ignore import { UrlParser } from './url_parser'; import { SearchCache } from './search_cache'; import { TimeCache } from './time_cache'; From c1b3a80bd5f5534e569042e4a020a93ef0a94934 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 11 Jun 2020 22:19:06 +0530 Subject: [PATCH 10/44] removed export --- src/plugins/data/public/search/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index bc5a2d2847adc..db1d04bb85958 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -27,7 +27,6 @@ export { ISearchContext, TSearchStrategyProvider, ISearchStrategy, - AggsParamsMapping, } from './types'; export { From 4890096f706c6df48073a261249a231edb8ea3dc Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 11:35:00 +0530 Subject: [PATCH 11/44] Fixed conflict in cega request_handler --- src/plugins/vis_type_vega/public/vega_request_handler.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plugins/vis_type_vega/public/vega_request_handler.ts b/src/plugins/vis_type_vega/public/vega_request_handler.ts index 0645ef25cf80f..20faf536f219d 100644 --- a/src/plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/plugins/vis_type_vega/public/vega_request_handler.ts @@ -20,14 +20,11 @@ import { Filter, esQuery, TimeRange, Query } from '../../data/public'; import { SearchAPI } from './data_model/search_api'; - -// @ts-ignore import { TimeCache } from './data_model/time_cache'; import { VegaVisualizationDependencies } from './plugin'; import { VisParams } from './vega_fn'; -import { getData } from './services'; -import { getInjectedMetadata } from 'src/plugins/data/public/services'; +import { getData, getInjectedMetadata } from './services'; interface VegaRequestHandlerParams { query: Query; From 789a0b6a2135e52ae091c679841896c3c0616ed2 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 11:36:44 +0530 Subject: [PATCH 12/44] Fixed conflicts --- .../public/data_model/es_query_parser.ts | 18 +++++++++--------- .../vis_type_vega/public/data_model/types.ts | 9 +++++---- .../public/data_model/vega_parser.ts | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts index 25f6b1e310c16..edcaccc2e8b18 100644 --- a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts @@ -19,16 +19,16 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; - -import { isPlainObject, cloneDeep } from 'lodash'; +import { cloneDeep, isPlainObject } from 'lodash'; import { TimeCache } from './time_cache'; -import { SearchCache } from './search_cache'; -import { Opts, Type, Data, UrlObject, Requests, Bool } from './types'; +import { SearchAPI } from './search_api'; +import { Opts, Type, Data, UrlObject, Bool } from './types'; const TIMEFILTER: string = '%timefilter%'; const AUTOINTERVAL: string = '%autointerval%'; const MUST_CLAUSE: string = '%dashboard_context-must_clause%'; const MUST_NOT_CLAUSE: string = '%dashboard_context-must_not_clause%'; +const FILTER_CLAUSE: string = '%dashboard_context-filter_clause%'; // These values may appear in the 'url': { ... } object const LEGACY_CONTEXT: string = '%context_query%'; @@ -40,13 +40,13 @@ const TIMEFIELD: string = '%timefield%'; */ export class EsQueryParser { _timeCache: TimeCache; - _searchAPI: SearchCache; + _searchAPI: SearchAPI; _filters: Bool; _onWarning: (...args: string[]) => void; constructor( timeCache: TimeCache, - searchAPI: SearchCache, + searchAPI: SearchAPI, filters: Bool, onWarning: (...args: string[]) => void ) { @@ -180,7 +180,7 @@ export class EsQueryParser { // Use dashboard context const newQuery = cloneDeep(this._filters); if (timefield) { - newQuery.bool!.must.push(body.query); + newQuery.bool!.must!.push(body.query); } body.query = newQuery; } @@ -195,8 +195,8 @@ export class EsQueryParser { * @param {object[]} requests each object is generated by parseUrl() * @returns {Promise} */ - async populateData(requests: Requests) { - const esSearches = requests.map((r) => r.url); + async populateData(requests: any) { + const esSearches = requests.map((r: any) => r.url); const data$ = this._searchAPI.search(esSearches); const results = await data$.toPromise(); diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index d4a5d1bc71659..e9cbffdb413b0 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -81,11 +81,12 @@ export interface TimeBucket { } export interface Bool { + [index: string]: any; bool?: Bool; - must: DslQuery[]; - filter: Filter[]; - should: never[]; - must_not: Filter[]; + must?: DslQuery[]; + filter?: Filter[]; + should?: never[]; + must_not?: Filter[]; } export interface Query { diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index f251843c59b19..bedd4d37f5550 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -30,7 +30,7 @@ import { EsQueryParser } from './es_query_parser'; import { Utils } from './utils'; import { EmsFileParser } from './ems_file_parser'; import { UrlParser } from './url_parser'; -import { SearchCache } from './search_cache'; +import { SearchAPI } from './search_api'; import { TimeCache } from './time_cache'; import { IServiceSettings } from '../../../maps_legacy/public'; import { Bool, Data, VegaSpec } from './types'; @@ -72,7 +72,7 @@ export class VegaParser { constructor( spec: string, - searchAPI: SearchCache, + searchAPI: SearchAPI, timeCache: TimeCache, filters: Bool, serviceSettings: IServiceSettings From 40a4d7d8db54183c94073e0333c2ad4735daf781 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 11:38:48 +0530 Subject: [PATCH 13/44] Removed search cache --- .../public/data_model/search_cache.ts | 53 ------------------- 1 file changed, 53 deletions(-) delete mode 100644 src/plugins/vis_type_vega/public/data_model/search_cache.ts diff --git a/src/plugins/vis_type_vega/public/data_model/search_cache.ts b/src/plugins/vis_type_vega/public/data_model/search_cache.ts deleted file mode 100644 index 1287104826c05..0000000000000 --- a/src/plugins/vis_type_vega/public/data_model/search_cache.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import LruCache from 'lru-cache'; -import { LegacyApiCaller } from '../../../data/public'; -import { UrlObject, CacheOptions } from './types'; - -export class SearchCache { - _es: LegacyApiCaller; - _cache: LruCache>; - - constructor(es: LegacyApiCaller, cacheOpts: CacheOptions) { - this._es = es; - this._cache = new LruCache(cacheOpts); - } - - /** - * Execute multiple searches, possibly combining the results of the cached searches - * with the new ones already in cache - * @param {object[]} requests array of search requests - */ - search(requests: UrlObject[]) { - const promises: Array> = []; - - for (const request of requests) { - const key = JSON.stringify(request); - let pending = this._cache.get(key); - if (pending === undefined) { - pending = this._es.search(request); - this._cache.set(key, pending); - } - promises.push(pending); - } - - return Promise.all(promises); - } -} From 9d4d75cbf799882e5430936bdf43098b03ce4b4a Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 15:15:15 +0530 Subject: [PATCH 14/44] Pass test --- .../public/data_model/es_query_parser.ts | 4 +--- .../public/data_model/time_cache.ts | 17 +++++++++-------- .../vis_type_vega/public/data_model/types.ts | 5 +++++ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts index edcaccc2e8b18..7644f62547796 100644 --- a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts @@ -273,9 +273,7 @@ export class EsQueryParser { ); } const bounds = this._timeCache.getTimeBounds(); - obj.interval = EsQueryParser._roundInterval( - (bounds.max!.valueOf() - bounds.min!.valueOf()) / size - ); + obj.interval = EsQueryParser._roundInterval((bounds.max - bounds.min) / size); continue; } diff --git a/src/plugins/vis_type_vega/public/data_model/time_cache.ts b/src/plugins/vis_type_vega/public/data_model/time_cache.ts index db95a7d185230..723a54603d0c1 100644 --- a/src/plugins/vis_type_vega/public/data_model/time_cache.ts +++ b/src/plugins/vis_type_vega/public/data_model/time_cache.ts @@ -19,6 +19,7 @@ import { TimefilterContract, TimeRangeBounds } from '../../../data/public'; import { TimeRange } from '../../../data/common'; +import { CacheBounds } from './types'; /** * Optimization caching - always return the same value if queried within this time @@ -33,7 +34,7 @@ const AlwaysCacheMaxAge: number = 40; export class TimeCache { _timefilter: TimefilterContract; _maxAge: number; - _cachedBounds?: TimeRangeBounds; + _cachedBounds?: CacheBounds; _cacheTS: number; _timeRange?: TimeRange; @@ -53,10 +54,10 @@ export class TimeCache { * Get cached time range values * @returns {{min: number, max: number}} */ - getTimeBounds(): TimeRangeBounds { + getTimeBounds(): CacheBounds { const ts = this._now(); - let bounds: TimeRangeBounds | null = null; + let bounds: CacheBounds | null = null; if (this._cachedBounds) { const diff = ts - this._cacheTS; @@ -71,8 +72,8 @@ export class TimeCache { if (diff < this._maxAge) { bounds = this._getBounds(); if ( - Math.abs(bounds.min!.valueOf() - this._cachedBounds.min!.valueOf()) < this._maxAge && - Math.abs(bounds.max!.valueOf() - this._cachedBounds.max!.valueOf()) < this._maxAge + Math.abs(bounds.min - this._cachedBounds.min) < this._maxAge && + Math.abs(bounds.max - this._cachedBounds.max) < this._maxAge ) { return this._cachedBounds; } @@ -94,11 +95,11 @@ export class TimeCache { * @returns {{min: number, max: number}} * @private */ - _getBounds(): TimeRangeBounds { + _getBounds(): CacheBounds { const bounds = this._timefilter.calculateBounds(this._timeRange!); return { - min: bounds.min, - max: bounds.max, + min: bounds.min!.valueOf(), + max: bounds.max!.valueOf(), }; } } diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index e9cbffdb413b0..e3d2929ede6c3 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -150,3 +150,8 @@ export interface CacheOptions { max: number; maxAge: number; } + +export interface CacheBounds { + min: number; + max: number; +} From 39f5e85fda0a7157ae2de1e004a5b07fbee37b03 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 15:19:13 +0530 Subject: [PATCH 15/44] Removed unwanted change --- src/core/server/plugins/plugins_system.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/server/plugins/plugins_system.ts b/src/core/server/plugins/plugins_system.ts index 4324dca1cf4ad..e0401006ffac9 100644 --- a/src/core/server/plugins/plugins_system.ts +++ b/src/core/server/plugins/plugins_system.ts @@ -92,8 +92,8 @@ export class PluginsSystem { createPluginSetupContext(this.coreContext, deps, plugin), pluginDepContracts ), - timeout: 3000 * Sec, - errorMessage: `Setup lifecycle of "${pluginName}" plugin wasn't completed in 3000sec. Consider disabling the plugin and re-start.`, + timeout: 30 * Sec, + errorMessage: `Setup lifecycle of "${pluginName}" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.`, }); contracts.set(pluginName, contract); From caaf4bdbfa0bc9cdecadeac654ff038118c2dbe4 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 15:32:40 +0530 Subject: [PATCH 16/44] Removed useage of TimeRangeBounds --- src/plugins/vis_type_vega/public/data_model/time_cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_type_vega/public/data_model/time_cache.ts b/src/plugins/vis_type_vega/public/data_model/time_cache.ts index 723a54603d0c1..27012d3cdc6c2 100644 --- a/src/plugins/vis_type_vega/public/data_model/time_cache.ts +++ b/src/plugins/vis_type_vega/public/data_model/time_cache.ts @@ -17,7 +17,7 @@ * under the License. */ -import { TimefilterContract, TimeRangeBounds } from '../../../data/public'; +import { TimefilterContract } from '../../../data/public'; import { TimeRange } from '../../../data/common'; import { CacheBounds } from './types'; From 78d6ac68a6ad007549054b9a37888e071ab3070d Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 15:35:42 +0530 Subject: [PATCH 17/44] Removed exports --- src/plugins/data/public/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 99adb73a2c54d..6ecb915f532bb 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -370,7 +370,6 @@ export { TabbedTable, SearchInterceptor, RequestTimeoutError, - LegacyApiCaller, AggParamsMapping, } from './search'; @@ -437,8 +436,6 @@ export { TimeHistory, TimefilterContract, TimeHistoryContract, - TimefilterSetup, - TimeRangeBounds, } from './query'; export { From 0d99cb73b6de0ec868a129da96dd238248f8c911 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 15:36:54 +0530 Subject: [PATCH 18/44] Removed export --- src/plugins/data/public/search/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 1f0016699eb55..1b5395e1071c5 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -61,4 +61,3 @@ export { export { SearchInterceptor } from './search_interceptor'; export { RequestTimeoutError } from './request_timeout_error'; -export { LegacyApiCaller } from './legacy/es_client'; From b5b80cd01c070acc3063f3721798bcceb734aa32 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 12 Jun 2020 17:54:40 +0530 Subject: [PATCH 19/44] Removed unused type --- src/plugins/vis_type_vega/public/data_model/types.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index e3d2929ede6c3..3970dee9c45b3 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -141,11 +141,6 @@ export interface Data { url?: UrlObject; } -export interface Requests { - dataObject?: Data; - url: UrlObject; -} - export interface CacheOptions { max: number; maxAge: number; From 2d396e3cbdc28e63bcf01c6ec60f4ecb212126e6 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 16:22:02 +0530 Subject: [PATCH 20/44] Fixed import --- src/plugins/vis_type_vega/public/data_model/vega_parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index ef2fdecbe452b..4dd8efe21b639 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -24,8 +24,8 @@ import versionCompare from 'compare-versions'; import hjson from 'hjson'; import { VISUALIZATION_COLORS } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import * as vega from 'vega-lib'; -import * as vegaLite from 'vega-lite'; +// @ts-ignore +import { vega, vegaLite } from '../lib/vega'; import { EsQueryParser } from './es_query_parser'; import { Utils } from './utils'; import { EmsFileParser } from './ems_file_parser'; From b177ec0bf00e781a91451330956c8904b6adc82d Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 16:29:58 +0530 Subject: [PATCH 21/44] Add type Requests --- .../vis_type_vega/public/data_model/ems_file_parser.ts | 6 +++--- src/plugins/vis_type_vega/public/data_model/types.ts | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/ems_file_parser.ts b/src/plugins/vis_type_vega/public/data_model/ems_file_parser.ts index abea4d4eb70ec..59256d47de97c 100644 --- a/src/plugins/vis_type_vega/public/data_model/ems_file_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/ems_file_parser.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore import { bypassExternalUrlCheck } from '../vega_view/vega_base_view'; import { IServiceSettings, FileLayer } from '../../../maps_legacy/public'; -import { Data } from './types'; +import { Data, UrlObject, Requests } from './types'; /** * This class processes all Vega spec customizations, @@ -39,7 +39,7 @@ export class EmsFileParser { /** * Update request object, expanding any context-aware keywords */ - parseUrl(obj: Data, url: any) { + parseUrl(obj: Data, url: UrlObject) { if (typeof url.name !== 'string') { throw new Error( i18n.translate('visTypeVega.emsFileParser.missingNameOfFileErrorMessage', { @@ -65,7 +65,7 @@ export class EmsFileParser { * @param {object[]} requests each object is generated by parseUrl() * @returns {Promise} */ - async populateData(requests: any) { + async populateData(requests: Requests[]) { if (requests.length === 0) return; const layers = await this._fileLayersP; diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index 3970dee9c45b3..dc56be354ee20 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -100,6 +100,7 @@ export interface UrlObject { [CONSTANTS.CONTEXT]?: boolean; [CONSTANTS.LEGACY_CONTEXT]?: string; [CONSTANTS.TYPE]?: string; + name?: string; index?: string; body?: { aggs?: AggParamsMapping; @@ -150,3 +151,10 @@ export interface CacheBounds { min: number; max: number; } + +export interface Requests { + obj: { + url: string; + }; + name: string; +} From cf448877474454501712d4ce70420951bff40f66 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 17:00:12 +0530 Subject: [PATCH 22/44] Added ContextVarsObject type --- .../public/data_model/es_query_parser.ts | 17 ++++++++------ .../vis_type_vega/public/data_model/types.ts | 23 ++++++++++++++----- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts index 7644f62547796..8af172a433444 100644 --- a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts @@ -20,9 +20,10 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { cloneDeep, isPlainObject } from 'lodash'; +import { AggParamsMapping } from 'src/plugins/data/public'; import { TimeCache } from './time_cache'; import { SearchAPI } from './search_api'; -import { Opts, Type, Data, UrlObject, Bool } from './types'; +import { Opts, Type, Data, UrlObject, Bool, Requests, Query, ContextVarsObject } from './types'; const TIMEFILTER: string = '%timefilter%'; const AUTOINTERVAL: string = '%autointerval%'; @@ -195,8 +196,8 @@ export class EsQueryParser { * @param {object[]} requests each object is generated by parseUrl() * @returns {Promise} */ - async populateData(requests: any) { - const esSearches = requests.map((r: any) => r.url); + async populateData(requests: Requests[]) { + const esSearches = requests.map((r: Requests) => r.url); const data$ = this._searchAPI.search(esSearches); const results = await data$.toPromise(); @@ -211,7 +212,7 @@ export class EsQueryParser { * @param {*} obj * @param {boolean} isQuery - if true, the `obj` belongs to the req's query portion */ - _injectContextVars(obj: any, isQuery: boolean) { + _injectContextVars(obj: Query | AggParamsMapping, isQuery: boolean) { if (obj && typeof obj === 'object') { if (Array.isArray(obj)) { // For arrays, replace MUST_CLAUSE and MUST_NOT_CLAUSE string elements @@ -252,7 +253,7 @@ export class EsQueryParser { } } else { for (const prop of Object.keys(obj)) { - const subObj = obj[prop]; + const subObj = (obj as ContextVarsObject)[prop]; if (!subObj || typeof obj !== 'object') continue; // replace "interval": { "%autointerval%": true|integer } with @@ -273,7 +274,9 @@ export class EsQueryParser { ); } const bounds = this._timeCache.getTimeBounds(); - obj.interval = EsQueryParser._roundInterval((bounds.max - bounds.min) / size); + (obj as ContextVarsObject).interval = EsQueryParser._roundInterval( + (bounds.max - bounds.min) / size + ); continue; } @@ -282,7 +285,7 @@ export class EsQueryParser { case 'min': case 'max': // Replace {"%timefilter%": "min|max", ...} object with a timestamp - obj[prop] = this._getTimeBound(subObj, subObj[TIMEFILTER]); + (obj as ContextVarsObject)[prop] = this._getTimeBound(subObj, subObj[TIMEFILTER]); continue; case true: // Replace {"%timefilter%": true, ...} object with the "range" object diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index dc56be354ee20..b2865d76d44bd 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -17,6 +17,7 @@ * under the License. */ +import { ShardsResponse, SearchResponse } from 'elasticsearch'; import { AggParamsMapping, Filter } from 'src/plugins/data/public'; import { DslQuery } from 'src/plugins/data/common'; @@ -59,6 +60,7 @@ export enum CONSTANTS { LEGACY_CONTEXT = '%context_query%', TYPE = '%type%', SYMBOL = 'Symbol(vega_id)', + AUTOINTERVAL = '%auautointerval%', } export interface Opts { @@ -133,12 +135,7 @@ export interface Data { timed_out?: boolean; took?: number; }; - _shards?: { - failed?: number; - skipped?: number; - successful?: number; - total?: number; - }; + _shards?: ShardsResponse; url?: UrlObject; } @@ -156,5 +153,19 @@ export interface Requests { obj: { url: string; }; + url: string; name: string; + dataObject: { + values: SearchResponse; + }; +} + +export interface ContextVarsObject { + [index: string]: any; + prop: + | string + | { + [CONSTANTS.AUTOINTERVAL]: number; + }; + interval: string; } From 5a3fe8b0e07b8254fefa05107d213efddf1f4494 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 17:13:43 +0530 Subject: [PATCH 23/44] Reuses urlObject and removed unwanted change on error --- .../vis_type_vega/public/data_model/url_parser.ts | 3 ++- src/plugins/vis_type_vega/public/data_model/utils.ts | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/url_parser.ts b/src/plugins/vis_type_vega/public/data_model/url_parser.ts index b12d55cf375eb..a27376bf25061 100644 --- a/src/plugins/vis_type_vega/public/data_model/url_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/url_parser.ts @@ -19,6 +19,7 @@ import $ from 'jquery'; import { i18n } from '@kbn/i18n'; +import { UrlObject } from './types'; /** * This class processes all Vega spec customizations, @@ -34,7 +35,7 @@ export class UrlParser { /** * Update request object */ - parseUrl(obj: any, urlObj: any) { + parseUrl(obj: UrlObject, urlObj: UrlObject) { let url = urlObj.url; if (!url) { throw new Error( diff --git a/src/plugins/vis_type_vega/public/data_model/utils.ts b/src/plugins/vis_type_vega/public/data_model/utils.ts index 3f03310a7156b..4d24b1237daeb 100644 --- a/src/plugins/vis_type_vega/public/data_model/utils.ts +++ b/src/plugins/vis_type_vega/public/data_model/utils.ts @@ -40,12 +40,11 @@ export class Utils { } static formatErrorToStr(...args: any[]) { - const err: Error = args[0]; - let error: string; - if (!err) { + let error: Error | string = args[0]; + if (!error) { error = 'ERR'; - } else { - error = err.message; + } else if (error instanceof Error) { + error = error.message; } return Utils.formatWarningToStr(error, ...Array.from(args).slice(1)); } From 420b2853592518e24711e7bd8b2625d15960f1c1 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 18:43:48 +0530 Subject: [PATCH 24/44] Add more types --- .../vis_type_vega/public/data_model/types.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index b2865d76d44bd..c25c9638e005e 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -169,3 +169,31 @@ export interface ContextVarsObject { }; interval: string; } + +type ToolTipPositions = 'top' | 'right' | 'bottom' | 'left'; + +export interface TooltipConfig { + position?: ToolTipPositions; + padding?: any; + centerOnMark?: any; +} + +export interface DstObj { + [index: string]: any; + type?: string; + latitude?: number; + longitude?: number; + zoom?: number; + mapStyle?: string | boolean; + minZoom?: number; + maxZoom?: number; + zoomControl?: boolean; + scrollWheelZoom?: boolean; + delayRepaint?: boolean; +} + +export interface VegaConfig extends DstObj { + [index: string]: any; + maxBounds?: number; + tooltips?: TooltipConfig | boolean; +} From 8cd91914b6f7b393eff7036fc6d19b243cbe3694 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 23:44:19 +0530 Subject: [PATCH 25/44] Added PendingType and UrlParserConfig --- .../vis_type_vega/public/data_model/types.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index c25c9638e005e..3696ee9b52c32 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -20,6 +20,9 @@ import { ShardsResponse, SearchResponse } from 'elasticsearch'; import { AggParamsMapping, Filter } from 'src/plugins/data/public'; import { DslQuery } from 'src/plugins/data/common'; +import { EsQueryParser } from './es_query_parser'; +import { EmsFileParser } from './ems_file_parser'; +import { UrlParser } from './url_parser'; interface Coordinate { axis: { @@ -192,8 +195,28 @@ export interface DstObj { delayRepaint?: boolean; } +export type ControlsLocation = 'row' | 'column' | 'row-reverse' | 'column-reverse'; +export type ControlsDirection = 'horizontal' | 'vertical'; + export interface VegaConfig extends DstObj { [index: string]: any; maxBounds?: number; tooltips?: TooltipConfig | boolean; + controlsLocation?: ControlsLocation; + controlsDirection?: ControlsDirection; +} + +export interface UrlParserConfig { + [index: string]: any; + elasticsearch: EsQueryParser; + emsfile: EmsFileParser; + url: UrlParser; +} + +export interface PendingType { + [index: string]: any; + dataObject?: Data; + obj?: Data; + url?: UrlObject; + name?: string; } From f802c380e3ee0c7151ed44ab9c1129f0481faa56 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 23:45:14 +0530 Subject: [PATCH 26/44] Updated types --- .../public/data_model/vega_parser.ts | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 4dd8efe21b639..5e4a647d7e971 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -33,7 +33,18 @@ import { UrlParser } from './url_parser'; import { SearchAPI } from './search_api'; import { TimeCache } from './time_cache'; import { IServiceSettings } from '../../../maps_legacy/public'; -import { Bool, Data, VegaSpec } from './types'; +import { + Bool, + Data, + VegaSpec, + VegaConfig, + TooltipConfig, + DstObj, + UrlParserConfig, + PendingType, + ControlsLocation, + ControlsDirection, +} from './types'; // Set default single color to match other Kibana visualizations const defaultColor: string = VISUALIZATION_COLORS[0]; @@ -44,7 +55,7 @@ const locToDirMap: any = { top: 'column-reverse', bottom: 'column', }; -const DEFAULT_SCHEMA = 'https://vega.github.io/schema/vega/v5.json'; +const DEFAULT_SCHEMA: string = 'https://vega.github.io/schema/vega/v5.json'; // If there is no "%type%" parameter, use this parser const DEFAULT_PARSER: string = 'elasticsearch'; @@ -54,20 +65,20 @@ export class VegaParser { hideWarnings: boolean; error?: string; warnings: string[]; - _urlParsers: { [key: string]: any }; + _urlParsers: UrlParserConfig; isVegaLite?: boolean; useHover?: boolean; - _config?: any; + _config?: VegaConfig; useMap?: boolean; renderer?: string; - tooltips?: object; + tooltips?: boolean | TooltipConfig; mapConfig?: object; - vlspec?: any; + vlspec?: VegaSpec; useResize?: boolean; paddingWidth?: number; paddingHeight?: number; - containerDir?: any; - controlsDir?: string; + containerDir?: ControlsLocation | ControlsDirection; + controlsDir?: ControlsLocation; constructor( spec: string, @@ -145,7 +156,7 @@ export class VegaParser { * @private */ _compileVegaLite() { - this.vlspec = this.spec; + this.vlspec = this.spec as VegaSpec; // eslint-disable-next-line import/namespace const logger = vega.logger(vega.Warn); // note: eslint has a false positive here logger.warn = this._onWarning.bind(this); @@ -347,7 +358,7 @@ export class VegaParser { return false; } - const result = this._config.tooltips || {}; + const result: TooltipConfig = (this._config?.tooltips as TooltipConfig) || {}; if (result.position === undefined) { result.position = 'top'; @@ -401,12 +412,12 @@ export class VegaParser { * @private */ _parseMapConfig() { - const res: { [key: string]: any } = { - delayRepaint: this._config.delayRepaint === undefined ? true : this._config.delayRepaint, + const res: VegaConfig = { + delayRepaint: this._config?.delayRepaint === undefined ? true : this._config.delayRepaint, }; const validate = (name: string, isZoom: boolean) => { - const val = this._config[name]; + const val = this._config ? this._config[name] : undefined; if (val !== undefined) { const parsed = parseFloat(val); if (Number.isFinite(parsed) && (!isZoom || (parsed >= 0 && parsed <= 30))) { @@ -430,7 +441,7 @@ export class VegaParser { validate(`maxZoom`, true); // `false` is a valid value - res.mapStyle = this._config.mapStyle === undefined ? `default` : this._config.mapStyle; + res.mapStyle = this._config?.mapStyle === undefined ? `default` : this._config.mapStyle; if (res.mapStyle !== `default` && res.mapStyle !== false) { this._onWarning( i18n.translate('visTypeVega.vegaParser.mapStyleValueTypeWarningMessage', { @@ -449,7 +460,7 @@ export class VegaParser { this._parseBool('zoomControl', res, true); this._parseBool('scrollWheelZoom', res, false); - const maxBounds = this._config.maxBounds; + const maxBounds = this._config?.maxBounds; if (maxBounds !== undefined) { if ( !Array.isArray(maxBounds) || @@ -472,8 +483,8 @@ export class VegaParser { return res; } - _parseBool(paramName: string, dstObj: any, dflt: any) { - const val = this._config[paramName]; + _parseBool(paramName: string, dstObj: DstObj, dflt: boolean | string | number) { + const val = this._config ? this._config[paramName] : undefined; if (val === undefined) { dstObj[paramName] = dflt; } else if (typeof val !== 'boolean') { @@ -535,7 +546,7 @@ export class VegaParser { * @private */ async _resolveDataUrls() { - const pending: { [key: string]: any } = {}; + const pending: PendingType = {}; this._findObjectDataUrls(this.spec, (obj: Data) => { const url = obj.url; @@ -588,7 +599,7 @@ export class VegaParser { this._findObjectDataUrls(elem, onFind, key); } } else if (_.isPlainObject(obj)) { - if (key === 'data' && _.isPlainObject(obj.url)) { + if (key === 'data' && _.isPlainObject(obj?.url)) { // Assume that any "data": {"url": {...}} is a request for data if (obj.values !== undefined || obj.source !== undefined) { throw new Error( From 38c8ba179eaebf96ca84fdbc1fe52cc06021b6c0 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 23:49:27 +0530 Subject: [PATCH 27/44] Updated type of locToDirMap --- src/plugins/vis_type_vega/public/data_model/vega_parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 5e4a647d7e971..d173126a409d6 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -49,7 +49,7 @@ import { // Set default single color to match other Kibana visualizations const defaultColor: string = VISUALIZATION_COLORS[0]; -const locToDirMap: any = { +const locToDirMap: Record = { left: 'row-reverse', right: 'row', top: 'column-reverse', @@ -287,7 +287,7 @@ export class VegaParser { defaultMessage: 'Unrecognized {controlsLocationParam} value. Expecting one of [{locToDirMap}]', values: { - locToDirMap: `"${locToDirMap.keys().join('", "')}"`, + locToDirMap: `"${Object.keys(locToDirMap).join('", "')}"`, controlsLocationParam: 'controlsLocation', }, }) From c9f7e3095bac0c9f7c3fb6af5e52bfb36e81adb7 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 22 Jun 2020 23:59:43 +0530 Subject: [PATCH 28/44] Removed unused types --- .../vis_type_vega/public/data_model/types.ts | 36 +++++-------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index 3696ee9b52c32..17a90aaef3920 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -17,13 +17,19 @@ * under the License. */ -import { ShardsResponse, SearchResponse } from 'elasticsearch'; +import { SearchResponse } from 'elasticsearch'; import { AggParamsMapping, Filter } from 'src/plugins/data/public'; import { DslQuery } from 'src/plugins/data/common'; import { EsQueryParser } from './es_query_parser'; import { EmsFileParser } from './ems_file_parser'; import { UrlParser } from './url_parser'; +interface Body { + aggs?: AggParamsMapping; + query?: Query; + timeout?: string; +} + interface Coordinate { axis: { title: string; @@ -107,38 +113,12 @@ export interface UrlObject { [CONSTANTS.TYPE]?: string; name?: string; index?: string; - body?: { - aggs?: AggParamsMapping; - query?: Query; - timeout?: string; - }; + body?: Body; size?: number; timeout?: string; } export interface Data { - format?: { - help?: string; - property?: string; - }; - values?: - | { - aggregations?: { - time_buckets: TimeBucket[]; - }; - } - | string; - hits?: { - hits?: any[]; - max_score?: number; - total?: { - relation: string; - value: number; - }; - timed_out?: boolean; - took?: number; - }; - _shards?: ShardsResponse; url?: UrlObject; } From 3f1b6e226254cfc8a5e0cc7495ee2f9b9c1a4b69 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Tue, 23 Jun 2020 21:39:41 +0530 Subject: [PATCH 29/44] Add types for hjson --- package.json | 3 ++- src/plugins/vis_type_vega/public/data_model/vega_parser.ts | 1 - yarn.lock | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3eaa1fb05e906..0b258f4fabbfa 100644 --- a/package.json +++ b/package.json @@ -344,6 +344,7 @@ "@types/hapi-auth-cookie": "^9.1.0", "@types/has-ansi": "^3.0.0", "@types/history": "^4.7.3", + "@types/hjson": "^2.4.2", "@types/hoek": "^4.1.3", "@types/inert": "^5.1.2", "@types/jest": "^25.2.3", @@ -454,9 +455,9 @@ "is-path-inside": "^2.1.0", "istanbul-instrumenter-loader": "3.0.1", "jest": "^25.5.4", - "jest-environment-jsdom-thirteen": "^1.0.1", "jest-circus": "^25.5.4", "jest-cli": "^25.5.4", + "jest-environment-jsdom-thirteen": "^1.0.1", "jest-raw-loader": "^1.0.1", "jimp": "^0.9.6", "json5": "^1.0.1", diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index d173126a409d6..d50395a3e72f3 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -20,7 +20,6 @@ import _ from 'lodash'; import schemaParser from 'vega-schema-url-parser'; import versionCompare from 'compare-versions'; -// @ts-ignore import hjson from 'hjson'; import { VISUALIZATION_COLORS } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; diff --git a/yarn.lock b/yarn.lock index b600ccb75c9fa..15933ee2f536c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5134,6 +5134,11 @@ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.3.tgz#856c99cdc1551d22c22b18b5402719affec9839a" integrity sha512-cS5owqtwzLN5kY+l+KgKdRJ/Cee8tlmQoGQuIE9tWnSmS3JMKzmxo2HIAk2wODMifGwO20d62xZQLYz+RLfXmw== +"@types/hjson@^2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@types/hjson/-/hjson-2.4.2.tgz#fd0288a5b6778cda993c978e43cc978ddc8f22e9" + integrity sha512-MSKTfEyR8DbzJTOAY47BIJBD72ol4cu6BOw5inda0q1eEtEmurVHL4OmYB3Lxa4/DwXbWidkddvtoygbGQEDIw== + "@types/hoek@^4.1.3": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/hoek/-/hoek-4.1.3.tgz#d1982d48fb0d2a0e5d7e9d91838264d8e428d337" From 050fdf06c8edea8860341a96612dd7bca6d9d4bc Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 25 Jun 2020 02:01:17 +0530 Subject: [PATCH 30/44] Replaced AggParamsMapping with SearchParams --- src/plugins/vis_type_vega/public/data_model/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index 17a90aaef3920..642852e29c9ae 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -17,15 +17,15 @@ * under the License. */ -import { SearchResponse } from 'elasticsearch'; -import { AggParamsMapping, Filter } from 'src/plugins/data/public'; +import { SearchResponse, SearchParams } from 'elasticsearch'; +import { Filter } from 'src/plugins/data/public'; import { DslQuery } from 'src/plugins/data/common'; import { EsQueryParser } from './es_query_parser'; import { EmsFileParser } from './ems_file_parser'; import { UrlParser } from './url_parser'; interface Body { - aggs?: AggParamsMapping; + aggs?: SearchParams['body']['aggs']; query?: Query; timeout?: string; } From b4d76ff8fbc0b2a7f25858c6e0cad9a027fc6562 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 25 Jun 2020 10:21:12 +0530 Subject: [PATCH 31/44] Removed all usage of AggParamsMapping --- src/plugins/data/public/index.ts | 1 - .../vis_type_vega/public/data_model/es_query_parser.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 6ecb915f532bb..1554ac71f8c55 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -370,7 +370,6 @@ export { TabbedTable, SearchInterceptor, RequestTimeoutError, - AggParamsMapping, } from './search'; // Search namespace diff --git a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts index 8af172a433444..4fdd68f9e9dbe 100644 --- a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts @@ -20,7 +20,7 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { cloneDeep, isPlainObject } from 'lodash'; -import { AggParamsMapping } from 'src/plugins/data/public'; +import { SearchParams } from 'elasticsearch'; import { TimeCache } from './time_cache'; import { SearchAPI } from './search_api'; import { Opts, Type, Data, UrlObject, Bool, Requests, Query, ContextVarsObject } from './types'; @@ -212,7 +212,7 @@ export class EsQueryParser { * @param {*} obj * @param {boolean} isQuery - if true, the `obj` belongs to the req's query portion */ - _injectContextVars(obj: Query | AggParamsMapping, isQuery: boolean) { + _injectContextVars(obj: Query | SearchParams['body']['aggs'], isQuery: boolean) { if (obj && typeof obj === 'object') { if (Array.isArray(obj)) { // For arrays, replace MUST_CLAUSE and MUST_NOT_CLAUSE string elements From 7a37be3e3c68ffd89e16a31012de53a4dbd20d70 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Tue, 30 Jun 2020 03:13:08 +0530 Subject: [PATCH 32/44] Updated types --- .../vis_type_vega/public/data_model/types.ts | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index 642852e29c9ae..309e68aeb5488 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -47,6 +47,41 @@ interface AutoSize { contains: string; } +interface Padding { + left: number; + right: number; + top: number; + bottom: number; +} + +interface Mark { + color?: string; + fill?: string; +} + +type Renderer = 'svg' | 'canvas'; + +export interface KibanaConfig { + controlsLocation: ControlsLocation; + controlsDirection: ControlsDirection; + hideWarnings: boolean; + type: string; + renderer: Renderer; +} + +interface VegaSpecConfig extends KibanaConfig { + kibana: KibanaConfig; + padding: Padding; + projection: Projection; + autosize: AutoSize; + tooltips: TooltipConfig; + mark: Mark; +} + +interface Projection { + name: string; +} + export interface VegaSpec { [index: string]: any; $schema: string; @@ -55,12 +90,12 @@ export interface VegaSpec { mark?: string; title?: string; autosize: AutoSize; - projections?: any; - width?: any; - height?: any; - padding?: any; - _hostConfig?: any; - config?: any; + projections?: Projection[]; + width?: number; + height?: number; + padding?: number | Padding; + _hostConfig?: KibanaConfig; + config?: VegaSpecConfig; } export enum CONSTANTS { @@ -119,7 +154,10 @@ export interface UrlObject { } export interface Data { + [index: string]: any; url?: UrlObject; + values?: unknown; + source?: unknown; } export interface CacheOptions { From 695926b52cd6c29e5701cb55a25d5d57dd4d7b8b Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Tue, 30 Jun 2020 03:14:54 +0530 Subject: [PATCH 33/44] Add parseSpec --- .../public/data_model/vega_parser.ts | 116 +++++++++--------- 1 file changed, 57 insertions(+), 59 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index d50395a3e72f3..5ae510fbb71fd 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -43,6 +43,7 @@ import { PendingType, ControlsLocation, ControlsDirection, + KibanaConfig, } from './types'; // Set default single color to match other Kibana visualizations @@ -60,7 +61,8 @@ const DEFAULT_SCHEMA: string = 'https://vega.github.io/schema/vega/v5.json'; const DEFAULT_PARSER: string = 'elasticsearch'; export class VegaParser { - spec: VegaSpec | string; + spec: string; + parsedSpec?: VegaSpec; hideWarnings: boolean; error?: string; warnings: string[]; @@ -113,10 +115,10 @@ export class VegaParser { async _parseAsync() { if (this.isVegaLite !== undefined) throw new Error(); - if (typeof this.spec === 'string') { - this.spec = hjson.parse(this.spec, { legacyRoot: false }); + if (!this.parsedSpec) { + this.parsedSpec = hjson.parse(this.spec, { legacyRoot: false }); } - if (!_.isPlainObject(this.spec)) { + if (!_.isPlainObject(this.parsedSpec)) { throw new Error( i18n.translate('visTypeVega.vegaParser.invalidVegaSpecErrorMessage', { defaultMessage: 'Invalid Vega specification', @@ -136,9 +138,9 @@ export class VegaParser { this._parseControlPlacement(); if (this.useMap) { this.mapConfig = this._parseMapConfig(); - } else if ((this.spec as VegaSpec).autosize === undefined) { + } else if (this.parsedSpec?.autosize === undefined) { // Default autosize should be fit, unless it's a map (leaflet-vega handles that) - (this.spec as VegaSpec).autosize = { type: 'fit', contains: 'padding' }; + this.parsedSpec!.autosize = { type: 'fit', contains: 'padding' }; } await this._resolveDataUrls(); @@ -155,24 +157,24 @@ export class VegaParser { * @private */ _compileVegaLite() { - this.vlspec = this.spec as VegaSpec; + this.vlspec = this.parsedSpec; // eslint-disable-next-line import/namespace const logger = vega.logger(vega.Warn); // note: eslint has a false positive here logger.warn = this._onWarning.bind(this); - this.spec = vegaLite.compile(this.vlspec, { logger }).spec; + this.parsedSpec = vegaLite.compile(this.vlspec, { logger }).spec; // When using VL with the type=map and user did not provid their own projection settings, // remove the default projection that was generated by VegaLite compiler. // This way we let leaflet-vega library inject a different default projection for tile maps. // Also, VL injects default padding and autosize values, but neither should be set for vega-leaflet. if (this.useMap) { - const hasConfig = _.isPlainObject(this.vlspec.config); - if (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.projection)) { + const hasConfig = _.isPlainObject(this.vlspec?.config); + if (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.projection)) { // Assume VL generates spec.projections = an array of exactly one object named 'projection' if ( - !Array.isArray((this.spec as VegaSpec).projections) || - (this.spec as VegaSpec).projections.length !== 1 || - (this.spec as VegaSpec).projections[0].name !== 'projection' + !Array.isArray(this.parsedSpec?.projections) || + this.parsedSpec?.projections.length !== 1 || + this.parsedSpec?.projections[0].name !== 'projection' ) { throw new Error( i18n.translate( @@ -184,23 +186,23 @@ export class VegaParser { ) ); } - delete (this.spec as VegaSpec).projections; + delete this.parsedSpec.projections; } // todo: sizing cleanup might need to be rethought and consolidated - if (!this.vlspec.width) delete (this.spec as VegaSpec).width; - if (!this.vlspec.height) delete (this.spec as VegaSpec).height; + if (!this.vlspec?.width) delete this.parsedSpec?.width; + if (!this.vlspec?.height) delete this.parsedSpec?.height; if ( - !this.vlspec.padding && - (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.padding)) + !this.vlspec?.padding && + (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.padding)) ) { - delete (this.spec as VegaSpec).padding; + delete this.parsedSpec?.padding; } if ( - !this.vlspec.autosize && - (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.autosize)) + !this.vlspec?.autosize && + (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.autosize)) ) { - delete (this.spec as VegaSpec).autosize; + delete this.parsedSpec?.autosize; } } } @@ -216,10 +218,10 @@ export class VegaParser { // and will be automatically updated on resize events. // We delete width & height if the autosize is set to "fit" // We also set useResize=true in case autosize=none, and width & height are not set - const autosize = (this.spec as VegaSpec).autosize.type || (this.spec as VegaSpec).autosize; + const autosize = this.parsedSpec?.autosize.type || this.parsedSpec?.autosize; if ( autosize === 'fit' || - (autosize === 'none' && !(this.spec as VegaSpec).width && !(this.spec as VegaSpec).height) + (autosize === 'none' && !this.parsedSpec?.width && !this.parsedSpec?.height) ) { this.useResize = true; } @@ -230,26 +232,24 @@ export class VegaParser { this.paddingHeight = 0; if ( this.useResize && - (this.spec as VegaSpec).padding && - (this.spec as VegaSpec).autosize.contains !== 'padding' + this.parsedSpec?.padding && + this.parsedSpec.autosize.contains !== 'padding' ) { - if (typeof (this.spec as VegaSpec).padding === 'object') { + if (typeof this.parsedSpec.padding === 'object') { this.paddingWidth += - (+(this.spec as VegaSpec).padding.left || 0) + - (+(this.spec as VegaSpec).padding.right || 0); + (+this.parsedSpec.padding.left || 0) + (+this.parsedSpec.padding.right || 0); this.paddingHeight += - (+(this.spec as VegaSpec).padding.top || 0) + - (+(this.spec as VegaSpec).padding.bottom || 0); + (+this.parsedSpec.padding.top || 0) + (+this.parsedSpec.padding.bottom || 0); } else { - this.paddingWidth += 2 * (+(this.spec as VegaSpec).padding || 0); - this.paddingHeight += 2 * (+(this.spec as VegaSpec).padding || 0); + this.paddingWidth += 2 * (+this.parsedSpec.padding || 0); + this.paddingHeight += 2 * (+this.parsedSpec.padding || 0); } } - if (this.useResize && ((this.spec as VegaSpec).width || (this.spec as VegaSpec).height)) { + if (this.useResize && (this.parsedSpec?.width || this.parsedSpec?.height)) { if (this.isVegaLite) { - delete (this.spec as VegaSpec).width; - delete (this.spec as VegaSpec).height; + delete this.parsedSpec.width; + delete this.parsedSpec.height; } else { this._onWarning( i18n.translate( @@ -310,11 +310,11 @@ export class VegaParser { * @returns {object} kibana config * @private */ - _parseConfig(): object { - let result; - if ((this.spec as VegaSpec)._hostConfig !== undefined) { - result = (this.spec as VegaSpec)._hostConfig; - delete (this.spec as VegaSpec)._hostConfig; + _parseConfig(): KibanaConfig | {} { + let result: KibanaConfig | null = null; + if (this.parsedSpec?._hostConfig !== undefined) { + result = this.parsedSpec._hostConfig; + delete this.parsedSpec._hostConfig; if (!_.isPlainObject(result)) { throw new Error( i18n.translate('visTypeVega.vegaParser.hostConfigValueTypeErrorMessage', { @@ -334,12 +334,9 @@ export class VegaParser { }) ); } - if ( - _.isPlainObject((this.spec as VegaSpec).config) && - (this.spec as VegaSpec).config.kibana !== undefined - ) { - result = (this.spec as VegaSpec).config.kibana; - delete (this.spec as VegaSpec).config.kibana; + if (_.isPlainObject(this.parsedSpec?.config) && this.parsedSpec?.config?.kibana !== undefined) { + result = this.parsedSpec.config.kibana; + delete this.parsedSpec.config.kibana; if (!_.isPlainObject(result)) { throw new Error( i18n.translate('visTypeVega.vegaParser.kibanaConfigValueTypeErrorMessage', { @@ -507,7 +504,7 @@ export class VegaParser { * @private */ _parseSchema() { - if (!(this.spec as VegaSpec).$schema) { + if (!this.parsedSpec?.$schema) { this._onWarning( i18n.translate('visTypeVega.vegaParser.inputSpecDoesNotSpecifySchemaWarningMessage', { defaultMessage: @@ -515,10 +512,10 @@ export class VegaParser { values: { defaultSchema: `"${DEFAULT_SCHEMA}"`, schemaParam: '"$schema"' }, }) ); - (this.spec as VegaSpec).$schema = DEFAULT_SCHEMA; + this.parsedSpec!.$schema = DEFAULT_SCHEMA; } - const schema = schemaParser((this.spec as VegaSpec).$schema); + const schema = schemaParser(this.parsedSpec!.$schema); const isVegaLite = schema.library === 'vega-lite'; const libVersion = isVegaLite ? vegaLite.version : vega.version; @@ -547,7 +544,7 @@ export class VegaParser { async _resolveDataUrls() { const pending: PendingType = {}; - this._findObjectDataUrls(this.spec, (obj: Data) => { + this._findObjectDataUrls(this.parsedSpec!, (obj: Data) => { const url = obj.url; delete obj.url; let type = url!['%type%']; @@ -592,7 +589,8 @@ export class VegaParser { * @param {string} [key] field name of the current object * @private */ - _findObjectDataUrls(obj: any, onFind: (data: Data) => void, key?: any) { + + _findObjectDataUrls(obj: VegaSpec | Data, onFind: (data: Data) => void, key?: any) { if (Array.isArray(obj)) { for (const elem of obj) { this._findObjectDataUrls(elem, onFind, key); @@ -616,7 +614,7 @@ export class VegaParser { ) ); } - onFind(obj); + onFind(obj as Data); } else { for (const k of Object.keys(obj)) { this._findObjectDataUrls(obj[k], onFind, k); @@ -641,9 +639,9 @@ export class VegaParser { // https://github.com/vega/vega/issues/1083 // Don't set defaults if spec.config.mark.color or fill are set if ( - !(this.spec as VegaSpec).config.mark || - ((this.spec as VegaSpec).config.mark.color === undefined && - (this.spec as VegaSpec).config.mark.fill === undefined) + !this.parsedSpec?.config?.mark || + (this.parsedSpec.config.mark.color === undefined && + this.parsedSpec.config.mark.fill === undefined) ) { this._setDefaultValue(defaultColor, 'config', 'arc', 'fill'); this._setDefaultValue(defaultColor, 'config', 'area', 'fill'); @@ -666,12 +664,12 @@ export class VegaParser { * @private */ _setDefaultValue(value: unknown, ...fields: string[]) { - let o = this.spec; + let o = this.parsedSpec; for (let i = 0; i < fields.length - 1; i++) { const field = fields[i]; - const subObj = (o as VegaSpec)[field]; + const subObj = o![field]; if (subObj === undefined) { - (o as VegaSpec)[field] = {}; + o![field] = {}; } else if (!_.isPlainObject(subObj)) { return; } From c90a7689753a7070d9081fd8e226e54462a4f8ad Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Tue, 30 Jun 2020 18:28:54 +0530 Subject: [PATCH 34/44] Replaced parseSpec and spec with spec and rawSpec --- .../public/data_model/vega_parser.ts | 94 +++++++++---------- 1 file changed, 42 insertions(+), 52 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 5ae510fbb71fd..20d7b6d93536a 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -61,8 +61,8 @@ const DEFAULT_SCHEMA: string = 'https://vega.github.io/schema/vega/v5.json'; const DEFAULT_PARSER: string = 'elasticsearch'; export class VegaParser { - spec: string; - parsedSpec?: VegaSpec; + spec?: VegaSpec; + rawSpec: string; hideWarnings: boolean; error?: string; warnings: string[]; @@ -88,7 +88,7 @@ export class VegaParser { filters: Bool, serviceSettings: IServiceSettings ) { - this.spec = spec; + this.rawSpec = spec; this.hideWarnings = false; this.error = undefined; @@ -115,10 +115,10 @@ export class VegaParser { async _parseAsync() { if (this.isVegaLite !== undefined) throw new Error(); - if (!this.parsedSpec) { - this.parsedSpec = hjson.parse(this.spec, { legacyRoot: false }); + if (!this.spec) { + this.spec = hjson.parse(this.rawSpec, { legacyRoot: false }); } - if (!_.isPlainObject(this.parsedSpec)) { + if (!_.isPlainObject(this.spec)) { throw new Error( i18n.translate('visTypeVega.vegaParser.invalidVegaSpecErrorMessage', { defaultMessage: 'Invalid Vega specification', @@ -138,9 +138,9 @@ export class VegaParser { this._parseControlPlacement(); if (this.useMap) { this.mapConfig = this._parseMapConfig(); - } else if (this.parsedSpec?.autosize === undefined) { + } else if (this.spec?.autosize === undefined) { // Default autosize should be fit, unless it's a map (leaflet-vega handles that) - this.parsedSpec!.autosize = { type: 'fit', contains: 'padding' }; + this.spec!.autosize = { type: 'fit', contains: 'padding' }; } await this._resolveDataUrls(); @@ -157,11 +157,11 @@ export class VegaParser { * @private */ _compileVegaLite() { - this.vlspec = this.parsedSpec; + this.vlspec = this.spec; // eslint-disable-next-line import/namespace const logger = vega.logger(vega.Warn); // note: eslint has a false positive here logger.warn = this._onWarning.bind(this); - this.parsedSpec = vegaLite.compile(this.vlspec, { logger }).spec; + this.spec = vegaLite.compile(this.vlspec, { logger }).spec; // When using VL with the type=map and user did not provid their own projection settings, // remove the default projection that was generated by VegaLite compiler. @@ -172,9 +172,9 @@ export class VegaParser { if (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.projection)) { // Assume VL generates spec.projections = an array of exactly one object named 'projection' if ( - !Array.isArray(this.parsedSpec?.projections) || - this.parsedSpec?.projections.length !== 1 || - this.parsedSpec?.projections[0].name !== 'projection' + !Array.isArray(this.spec?.projections) || + this.spec?.projections.length !== 1 || + this.spec?.projections[0].name !== 'projection' ) { throw new Error( i18n.translate( @@ -186,23 +186,23 @@ export class VegaParser { ) ); } - delete this.parsedSpec.projections; + delete this.spec?.projections; } // todo: sizing cleanup might need to be rethought and consolidated - if (!this.vlspec?.width) delete this.parsedSpec?.width; - if (!this.vlspec?.height) delete this.parsedSpec?.height; + if (!this.vlspec?.width) delete this.spec?.width; + if (!this.vlspec?.height) delete this.spec?.height; if ( !this.vlspec?.padding && (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.padding)) ) { - delete this.parsedSpec?.padding; + delete this.spec?.padding; } if ( !this.vlspec?.autosize && (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.autosize)) ) { - delete this.parsedSpec?.autosize; + delete this.spec?.autosize; } } } @@ -218,11 +218,8 @@ export class VegaParser { // and will be automatically updated on resize events. // We delete width & height if the autosize is set to "fit" // We also set useResize=true in case autosize=none, and width & height are not set - const autosize = this.parsedSpec?.autosize.type || this.parsedSpec?.autosize; - if ( - autosize === 'fit' || - (autosize === 'none' && !this.parsedSpec?.width && !this.parsedSpec?.height) - ) { + const autosize = this.spec?.autosize.type || this.spec?.autosize; + if (autosize === 'fit' || (autosize === 'none' && !this.spec?.width && !this.spec?.height)) { this.useResize = true; } } @@ -230,26 +227,20 @@ export class VegaParser { // Padding is not included in the width/height by default this.paddingWidth = 0; this.paddingHeight = 0; - if ( - this.useResize && - this.parsedSpec?.padding && - this.parsedSpec.autosize.contains !== 'padding' - ) { - if (typeof this.parsedSpec.padding === 'object') { - this.paddingWidth += - (+this.parsedSpec.padding.left || 0) + (+this.parsedSpec.padding.right || 0); - this.paddingHeight += - (+this.parsedSpec.padding.top || 0) + (+this.parsedSpec.padding.bottom || 0); + if (this.useResize && this.spec?.padding && this.spec.autosize.contains !== 'padding') { + if (typeof this.spec.padding === 'object') { + this.paddingWidth += (+this.spec.padding.left || 0) + (+this.spec.padding.right || 0); + this.paddingHeight += (+this.spec.padding.top || 0) + (+this.spec.padding.bottom || 0); } else { - this.paddingWidth += 2 * (+this.parsedSpec.padding || 0); - this.paddingHeight += 2 * (+this.parsedSpec.padding || 0); + this.paddingWidth += 2 * (+this.spec.padding || 0); + this.paddingHeight += 2 * (+this.spec.padding || 0); } } - if (this.useResize && (this.parsedSpec?.width || this.parsedSpec?.height)) { + if (this.useResize && (this.spec?.width || this.spec?.height)) { if (this.isVegaLite) { - delete this.parsedSpec.width; - delete this.parsedSpec.height; + delete this.spec.width; + delete this.spec.height; } else { this._onWarning( i18n.translate( @@ -312,9 +303,9 @@ export class VegaParser { */ _parseConfig(): KibanaConfig | {} { let result: KibanaConfig | null = null; - if (this.parsedSpec?._hostConfig !== undefined) { - result = this.parsedSpec._hostConfig; - delete this.parsedSpec._hostConfig; + if (this.spec?._hostConfig !== undefined) { + result = this.spec._hostConfig; + delete this.spec._hostConfig; if (!_.isPlainObject(result)) { throw new Error( i18n.translate('visTypeVega.vegaParser.hostConfigValueTypeErrorMessage', { @@ -334,9 +325,9 @@ export class VegaParser { }) ); } - if (_.isPlainObject(this.parsedSpec?.config) && this.parsedSpec?.config?.kibana !== undefined) { - result = this.parsedSpec.config.kibana; - delete this.parsedSpec.config.kibana; + if (_.isPlainObject(this.spec?.config) && this.spec?.config?.kibana !== undefined) { + result = this.spec.config.kibana; + delete this.spec.config.kibana; if (!_.isPlainObject(result)) { throw new Error( i18n.translate('visTypeVega.vegaParser.kibanaConfigValueTypeErrorMessage', { @@ -504,7 +495,7 @@ export class VegaParser { * @private */ _parseSchema() { - if (!this.parsedSpec?.$schema) { + if (!this.spec?.$schema) { this._onWarning( i18n.translate('visTypeVega.vegaParser.inputSpecDoesNotSpecifySchemaWarningMessage', { defaultMessage: @@ -512,10 +503,10 @@ export class VegaParser { values: { defaultSchema: `"${DEFAULT_SCHEMA}"`, schemaParam: '"$schema"' }, }) ); - this.parsedSpec!.$schema = DEFAULT_SCHEMA; + this.spec!.$schema = DEFAULT_SCHEMA; } - const schema = schemaParser(this.parsedSpec!.$schema); + const schema = schemaParser(this.spec!.$schema); const isVegaLite = schema.library === 'vega-lite'; const libVersion = isVegaLite ? vegaLite.version : vega.version; @@ -544,7 +535,7 @@ export class VegaParser { async _resolveDataUrls() { const pending: PendingType = {}; - this._findObjectDataUrls(this.parsedSpec!, (obj: Data) => { + this._findObjectDataUrls(this.spec!, (obj: Data) => { const url = obj.url; delete obj.url; let type = url!['%type%']; @@ -639,9 +630,8 @@ export class VegaParser { // https://github.com/vega/vega/issues/1083 // Don't set defaults if spec.config.mark.color or fill are set if ( - !this.parsedSpec?.config?.mark || - (this.parsedSpec.config.mark.color === undefined && - this.parsedSpec.config.mark.fill === undefined) + !this.spec?.config?.mark || + (this.spec.config.mark.color === undefined && this.spec.config.mark.fill === undefined) ) { this._setDefaultValue(defaultColor, 'config', 'arc', 'fill'); this._setDefaultValue(defaultColor, 'config', 'area', 'fill'); @@ -664,7 +654,7 @@ export class VegaParser { * @private */ _setDefaultValue(value: unknown, ...fields: string[]) { - let o = this.parsedSpec; + let o = this.spec; for (let i = 0; i < fields.length - 1; i++) { const field = fields[i]; const subObj = o![field]; From 179d57db6edf8da63aa963d8148403142a2c376e Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Tue, 30 Jun 2020 18:30:56 +0530 Subject: [PATCH 35/44] Replaced any with unknown --- src/plugins/vis_type_vega/public/data_model/vega_parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 20d7b6d93536a..8ac5c291035df 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -581,7 +581,7 @@ export class VegaParser { * @private */ - _findObjectDataUrls(obj: VegaSpec | Data, onFind: (data: Data) => void, key?: any) { + _findObjectDataUrls(obj: VegaSpec | Data, onFind: (data: Data) => void, key?: unknown) { if (Array.isArray(obj)) { for (const elem of obj) { this._findObjectDataUrls(elem, onFind, key); From 4e256c1650f6c8cfac361971af6a70fe1bc5044c Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Tue, 30 Jun 2020 18:41:54 +0530 Subject: [PATCH 36/44] Replaced more any --- src/plugins/vis_type_vega/public/data_model/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index 309e68aeb5488..8986923bc8f43 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -195,8 +195,8 @@ type ToolTipPositions = 'top' | 'right' | 'bottom' | 'left'; export interface TooltipConfig { position?: ToolTipPositions; - padding?: any; - centerOnMark?: any; + padding?: number | Padding; + centerOnMark?: boolean | number; } export interface DstObj { From 1dd2b487332708d0fb980c31f0bfa32cc62778d5 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Tue, 30 Jun 2020 18:47:28 +0530 Subject: [PATCH 37/44] Cleanup types and interfaces --- .../vis_type_vega/public/data_model/types.ts | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index 8986923bc8f43..bb1a88253f891 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -61,14 +61,6 @@ interface Mark { type Renderer = 'svg' | 'canvas'; -export interface KibanaConfig { - controlsLocation: ControlsLocation; - controlsDirection: ControlsDirection; - hideWarnings: boolean; - type: string; - renderer: Renderer; -} - interface VegaSpecConfig extends KibanaConfig { kibana: KibanaConfig; padding: Padding; @@ -82,6 +74,30 @@ interface Projection { name: string; } +interface RequestDataObject { + values: SearchResponse; +} + +interface RequestObject { + url: string; +} + +type ContextVarsObjectProps = + | string + | { + [CONSTANTS.AUTOINTERVAL]: number; + }; + +type ToolTipPositions = 'top' | 'right' | 'bottom' | 'left'; + +export interface KibanaConfig { + controlsLocation: ControlsLocation; + controlsDirection: ControlsDirection; + hideWarnings: boolean; + type: string; + renderer: Renderer; +} + export interface VegaSpec { [index: string]: any; $schema: string; @@ -170,29 +186,18 @@ export interface CacheBounds { max: number; } -export interface Requests { - obj: { - url: string; - }; - url: string; +export interface Requests extends RequestObject { + obj: RequestObject; name: string; - dataObject: { - values: SearchResponse; - }; + dataObject: RequestDataObject; } export interface ContextVarsObject { [index: string]: any; - prop: - | string - | { - [CONSTANTS.AUTOINTERVAL]: number; - }; + prop: ContextVarsObjectProps; interval: string; } -type ToolTipPositions = 'top' | 'right' | 'bottom' | 'left'; - export interface TooltipConfig { position?: ToolTipPositions; padding?: number | Padding; @@ -214,6 +219,7 @@ export interface DstObj { } export type ControlsLocation = 'row' | 'column' | 'row-reverse' | 'column-reverse'; + export type ControlsDirection = 'horizontal' | 'vertical'; export interface VegaConfig extends DstObj { From 85dd73f824950a8687886268ff7ab054917388b0 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 2 Jul 2020 15:23:37 +0530 Subject: [PATCH 38/44] Removed unwanted casts --- .../vis_type_vega/public/data_model/vega_parser.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 8ac5c291035df..70315f83442f1 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -654,20 +654,21 @@ export class VegaParser { * @private */ _setDefaultValue(value: unknown, ...fields: string[]) { + if (!this.spec) return; let o = this.spec; for (let i = 0; i < fields.length - 1; i++) { const field = fields[i]; - const subObj = o![field]; + const subObj = o[field]; if (subObj === undefined) { - o![field] = {}; + o[field] = {}; } else if (!_.isPlainObject(subObj)) { return; } - o = (o as VegaSpec)[field]; + o = o[field]; } const lastField = fields[fields.length - 1]; - if ((o as VegaSpec)[lastField] === undefined) { - (o as VegaSpec)[lastField] = value; + if (o[lastField] === undefined) { + o[lastField] = value; } } From 7d02d5a3d9b3e76575dc031d599ad4bda5bc351f Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Thu, 2 Jul 2020 16:28:26 +0530 Subject: [PATCH 39/44] Avoid usage of ! and ? --- .../vis_type_vega/public/data_model/types.ts | 4 +- .../public/data_model/vega_parser.ts | 183 +++++++++--------- 2 files changed, 97 insertions(+), 90 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index bb1a88253f891..9876faf0fc88f 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -106,12 +106,12 @@ export interface VegaSpec { mark?: string; title?: string; autosize: AutoSize; - projections?: Projection[]; + projections: Projection[]; width?: number; height?: number; padding?: number | Padding; _hostConfig?: KibanaConfig; - config?: VegaSpecConfig; + config: VegaSpecConfig; } export enum CONSTANTS { diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index 70315f83442f1..b122e20b5ba60 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -138,9 +138,9 @@ export class VegaParser { this._parseControlPlacement(); if (this.useMap) { this.mapConfig = this._parseMapConfig(); - } else if (this.spec?.autosize === undefined) { + } else if (this.spec && this.spec.autosize === undefined) { // Default autosize should be fit, unless it's a map (leaflet-vega handles that) - this.spec!.autosize = { type: 'fit', contains: 'padding' }; + this.spec.autosize = { type: 'fit', contains: 'padding' }; } await this._resolveDataUrls(); @@ -161,20 +161,21 @@ export class VegaParser { // eslint-disable-next-line import/namespace const logger = vega.logger(vega.Warn); // note: eslint has a false positive here logger.warn = this._onWarning.bind(this); - this.spec = vegaLite.compile(this.vlspec, { logger }).spec; + this.spec = vegaLite.compile(this.vlspec, logger).spec; // When using VL with the type=map and user did not provid their own projection settings, // remove the default projection that was generated by VegaLite compiler. // This way we let leaflet-vega library inject a different default projection for tile maps. // Also, VL injects default padding and autosize values, but neither should be set for vega-leaflet. if (this.useMap) { - const hasConfig = _.isPlainObject(this.vlspec?.config); - if (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.projection)) { + if (!this.spec || !this.vlspec) return; + const hasConfig = _.isPlainObject(this.vlspec.config); + if (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.projection)) { // Assume VL generates spec.projections = an array of exactly one object named 'projection' if ( - !Array.isArray(this.spec?.projections) || - this.spec?.projections.length !== 1 || - this.spec?.projections[0].name !== 'projection' + !Array.isArray(this.spec.projections) || + this.spec.projections.length !== 1 || + this.spec.projections[0].name !== 'projection' ) { throw new Error( i18n.translate( @@ -186,23 +187,23 @@ export class VegaParser { ) ); } - delete this.spec?.projections; + delete this.spec.projections; } // todo: sizing cleanup might need to be rethought and consolidated - if (!this.vlspec?.width) delete this.spec?.width; - if (!this.vlspec?.height) delete this.spec?.height; + if (!this.vlspec.width) delete this.spec.width; + if (!this.vlspec.height) delete this.spec.height; if ( - !this.vlspec?.padding && - (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.padding)) + !this.vlspec.padding && + (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.padding)) ) { - delete this.spec?.padding; + delete this.spec.padding; } if ( - !this.vlspec?.autosize && - (this.vlspec?.config === undefined || (hasConfig && !this.vlspec.config.autosize)) + !this.vlspec.autosize && + (this.vlspec.config === undefined || (hasConfig && !this.vlspec.config.autosize)) ) { - delete this.spec?.autosize; + delete this.spec.autosize; } } } @@ -213,49 +214,52 @@ export class VegaParser { */ _calcSizing() { this.useResize = false; - if (!this.useMap) { - // when useResize is true, vega's canvas size will be set based on the size of the container, - // and will be automatically updated on resize events. - // We delete width & height if the autosize is set to "fit" - // We also set useResize=true in case autosize=none, and width & height are not set - const autosize = this.spec?.autosize.type || this.spec?.autosize; - if (autosize === 'fit' || (autosize === 'none' && !this.spec?.width && !this.spec?.height)) { - this.useResize = true; - } - } // Padding is not included in the width/height by default this.paddingWidth = 0; this.paddingHeight = 0; - if (this.useResize && this.spec?.padding && this.spec.autosize.contains !== 'padding') { - if (typeof this.spec.padding === 'object') { - this.paddingWidth += (+this.spec.padding.left || 0) + (+this.spec.padding.right || 0); - this.paddingHeight += (+this.spec.padding.top || 0) + (+this.spec.padding.bottom || 0); - } else { - this.paddingWidth += 2 * (+this.spec.padding || 0); - this.paddingHeight += 2 * (+this.spec.padding || 0); + if (this.spec) { + if (!this.useMap) { + // when useResize is true, vega's canvas size will be set based on the size of the container, + // and will be automatically updated on resize events. + // We delete width & height if the autosize is set to "fit" + // We also set useResize=true in case autosize=none, and width & height are not set + const autosize = this.spec.autosize.type || this.spec.autosize; + if (autosize === 'fit' || (autosize === 'none' && !this.spec.width && !this.spec.height)) { + this.useResize = true; + } } - } - if (this.useResize && (this.spec?.width || this.spec?.height)) { - if (this.isVegaLite) { - delete this.spec.width; - delete this.spec.height; - } else { - this._onWarning( - i18n.translate( - 'visTypeVega.vegaParser.widthAndHeightParamsAreIgnoredWithAutosizeFitWarningMessage', - { - defaultMessage: - 'The {widthParam} and {heightParam} params are ignored with {autosizeParam}', - values: { - autosizeParam: 'autosize=fit', - widthParam: '"width"', - heightParam: '"height"', - }, - } - ) - ); + if (this.useResize && this.spec.padding && this.spec.autosize.contains !== 'padding') { + if (typeof this.spec.padding === 'object') { + this.paddingWidth += (+this.spec.padding.left || 0) + (+this.spec.padding.right || 0); + this.paddingHeight += (+this.spec.padding.top || 0) + (+this.spec.padding.bottom || 0); + } else { + this.paddingWidth += 2 * (+this.spec.padding || 0); + this.paddingHeight += 2 * (+this.spec.padding || 0); + } + } + + if (this.useResize && (this.spec.width || this.spec.height)) { + if (this.isVegaLite) { + delete this.spec.width; + delete this.spec.height; + } else { + this._onWarning( + i18n.translate( + 'visTypeVega.vegaParser.widthAndHeightParamsAreIgnoredWithAutosizeFitWarningMessage', + { + defaultMessage: + 'The {widthParam} and {heightParam} params are ignored with {autosizeParam}', + values: { + autosizeParam: 'autosize=fit', + widthParam: '"width"', + heightParam: '"height"', + }, + } + ) + ); + } } } } @@ -303,38 +307,40 @@ export class VegaParser { */ _parseConfig(): KibanaConfig | {} { let result: KibanaConfig | null = null; - if (this.spec?._hostConfig !== undefined) { - result = this.spec._hostConfig; - delete this.spec._hostConfig; - if (!_.isPlainObject(result)) { - throw new Error( - i18n.translate('visTypeVega.vegaParser.hostConfigValueTypeErrorMessage', { - defaultMessage: 'If present, {configName} must be an object', - values: { configName: '"_hostConfig"' }, + if (this.spec) { + if (this.spec._hostConfig !== undefined) { + result = this.spec._hostConfig; + delete this.spec._hostConfig; + if (!_.isPlainObject(result)) { + throw new Error( + i18n.translate('visTypeVega.vegaParser.hostConfigValueTypeErrorMessage', { + defaultMessage: 'If present, {configName} must be an object', + values: { configName: '"_hostConfig"' }, + }) + ); + } + this._onWarning( + i18n.translate('visTypeVega.vegaParser.hostConfigIsDeprecatedWarningMessage', { + defaultMessage: + '{deprecatedConfigName} has been deprecated. Use {newConfigName} instead.', + values: { + deprecatedConfigName: '"_hostConfig"', + newConfigName: 'config.kibana', + }, }) ); } - this._onWarning( - i18n.translate('visTypeVega.vegaParser.hostConfigIsDeprecatedWarningMessage', { - defaultMessage: - '{deprecatedConfigName} has been deprecated. Use {newConfigName} instead.', - values: { - deprecatedConfigName: '"_hostConfig"', - newConfigName: 'config.kibana', - }, - }) - ); - } - if (_.isPlainObject(this.spec?.config) && this.spec?.config?.kibana !== undefined) { - result = this.spec.config.kibana; - delete this.spec.config.kibana; - if (!_.isPlainObject(result)) { - throw new Error( - i18n.translate('visTypeVega.vegaParser.kibanaConfigValueTypeErrorMessage', { - defaultMessage: 'If present, {configName} must be an object', - values: { configName: 'config.kibana' }, - }) - ); + if (_.isPlainObject(this.spec.config) && this.spec.config.kibana !== undefined) { + result = this.spec.config.kibana; + delete this.spec.config.kibana; + if (!_.isPlainObject(result)) { + throw new Error( + i18n.translate('visTypeVega.vegaParser.kibanaConfigValueTypeErrorMessage', { + defaultMessage: 'If present, {configName} must be an object', + values: { configName: 'config.kibana' }, + }) + ); + } } } return result || {}; @@ -495,7 +501,8 @@ export class VegaParser { * @private */ _parseSchema() { - if (!this.spec?.$schema) { + if (!this.spec) return false; + if (!this.spec.$schema) { this._onWarning( i18n.translate('visTypeVega.vegaParser.inputSpecDoesNotSpecifySchemaWarningMessage', { defaultMessage: @@ -503,10 +510,10 @@ export class VegaParser { values: { defaultSchema: `"${DEFAULT_SCHEMA}"`, schemaParam: '"$schema"' }, }) ); - this.spec!.$schema = DEFAULT_SCHEMA; + this.spec.$schema = DEFAULT_SCHEMA; } - const schema = schemaParser(this.spec!.$schema); + const schema = schemaParser(this.spec.$schema); const isVegaLite = schema.library === 'vega-lite'; const libVersion = isVegaLite ? vegaLite.version : vega.version; @@ -587,7 +594,7 @@ export class VegaParser { this._findObjectDataUrls(elem, onFind, key); } } else if (_.isPlainObject(obj)) { - if (key === 'data' && _.isPlainObject(obj?.url)) { + if (key === 'data' && _.isPlainObject(obj.url)) { // Assume that any "data": {"url": {...}} is a request for data if (obj.values !== undefined || obj.source !== undefined) { throw new Error( @@ -630,7 +637,7 @@ export class VegaParser { // https://github.com/vega/vega/issues/1083 // Don't set defaults if spec.config.mark.color or fill are set if ( - !this.spec?.config?.mark || + !this.spec?.config.mark || (this.spec.config.mark.color === undefined && this.spec.config.mark.fill === undefined) ) { this._setDefaultValue(defaultColor, 'config', 'arc', 'fill'); From ac62a243206e8ca8708de6ba983f062252e00140 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 3 Jul 2020 20:58:47 +0530 Subject: [PATCH 40/44] Retry adding package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17a101a893249..b1c0b57dfc54e 100644 --- a/package.json +++ b/package.json @@ -139,9 +139,9 @@ "@kbn/babel-preset": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", - "@kbn/telemetry-tools": "1.0.0", "@kbn/interpreter": "1.0.0", "@kbn/pm": "1.0.0", + "@kbn/telemetry-tools": "1.0.0", "@kbn/test-subj-selector": "0.2.1", "@kbn/ui-framework": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", From 3f6838a29902d7b4bf8ac5f511b58681b2d007e7 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Fri, 3 Jul 2020 21:27:53 +0530 Subject: [PATCH 41/44] Updated renovate.json5 --- renovate.json5 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/renovate.json5 b/renovate.json5 index 5a807b4b090c1..1ba6dc0ff7e1b 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -426,6 +426,14 @@ '@types/history', ], }, + { + groupSlug: 'hjson', + groupName: 'hjson related packages', + packageNames: [ + 'hjson', + '@types/hjson', + ], + }, { groupSlug: 'inquirer', groupName: 'inquirer related packages', From d44c3263308aec52785a9f98fb3b961712e429b6 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 6 Jul 2020 14:52:47 +0530 Subject: [PATCH 42/44] Fixed type issue in vega fn --- src/plugins/vis_type_vega/public/vega_fn.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/vis_type_vega/public/vega_fn.ts b/src/plugins/vis_type_vega/public/vega_fn.ts index 6b1af6044a2c4..d077aa7aee004 100644 --- a/src/plugins/vis_type_vega/public/vega_fn.ts +++ b/src/plugins/vis_type_vega/public/vega_fn.ts @@ -23,6 +23,7 @@ import { ExpressionFunctionDefinition, KibanaContext, Render } from '../../expre import { VegaVisualizationDependencies } from './plugin'; import { createVegaRequestHandler } from './vega_request_handler'; import { TimeRange, Query } from '../../data/public'; +import { VegaParser } from './data_model/vega_parser'; type Input = KibanaContext | null; type Output = Promise>; @@ -34,7 +35,7 @@ interface Arguments { export type VisParams = Required; interface RenderValue { - visData: Input; + visData: VegaParser; visType: 'vega'; visConfig: VisParams; } From 8ee21e21a1f20ac4425562f175c426d0496fadb2 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Mon, 6 Jul 2020 16:13:03 +0530 Subject: [PATCH 43/44] fixed type isssue from vega vis editor --- .../vis_type_vega/public/components/vega_vis_editor.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx b/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx index 1da5e7544850a..5e770fcff556d 100644 --- a/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx +++ b/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx @@ -20,7 +20,6 @@ import React, { useCallback } from 'react'; import { EuiCodeEditor } from '@elastic/eui'; import compactStringify from 'json-stringify-pretty-compact'; -// @ts-ignore import hjson from 'hjson'; import 'brace/mode/hjson'; import { i18n } from '@kbn/i18n'; @@ -45,7 +44,11 @@ const hjsonStringifyOptions = { keepWsc: true, }; -function format(value: string, stringify: typeof compactStringify, options?: any) { +function format( + value: string, + stringify: typeof hjson.stringify | typeof compactStringify, + options?: any +) { try { const spec = hjson.parse(value, { legacyRoot: false, keepWsc: true }); return stringify(spec, options); From e5ca35d6ccbb950c771eb72207b4693b4a248f97 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty Date: Tue, 7 Jul 2020 23:05:06 +0530 Subject: [PATCH 44/44] Fixed spec to rawSpec --- .../vis_type_vega/public/data_model/vega_parser.ts | 12 +++++------- .../vis_type_vega/public/vega_request_handler.ts | 1 - 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index b122e20b5ba60..17166e1540755 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -61,8 +61,7 @@ const DEFAULT_SCHEMA: string = 'https://vega.github.io/schema/vega/v5.json'; const DEFAULT_PARSER: string = 'elasticsearch'; export class VegaParser { - spec?: VegaSpec; - rawSpec: string; + spec: VegaSpec; hideWarnings: boolean; error?: string; warnings: string[]; @@ -82,13 +81,13 @@ export class VegaParser { controlsDir?: ControlsLocation; constructor( - spec: string, + spec: VegaSpec | string, searchAPI: SearchAPI, timeCache: TimeCache, filters: Bool, serviceSettings: IServiceSettings ) { - this.rawSpec = spec; + this.spec = spec as VegaSpec; this.hideWarnings = false; this.error = undefined; @@ -115,8 +114,8 @@ export class VegaParser { async _parseAsync() { if (this.isVegaLite !== undefined) throw new Error(); - if (!this.spec) { - this.spec = hjson.parse(this.rawSpec, { legacyRoot: false }); + if (typeof this.spec === 'string') { + this.spec = hjson.parse(this.spec, { legacyRoot: false }); } if (!_.isPlainObject(this.spec)) { throw new Error( @@ -661,7 +660,6 @@ export class VegaParser { * @private */ _setDefaultValue(value: unknown, ...fields: string[]) { - if (!this.spec) return; let o = this.spec; for (let i = 0; i < fields.length - 1; i++) { const field = fields[i]; diff --git a/src/plugins/vis_type_vega/public/vega_request_handler.ts b/src/plugins/vis_type_vega/public/vega_request_handler.ts index 20faf536f219d..997b1982d749a 100644 --- a/src/plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/plugins/vis_type_vega/public/vega_request_handler.ts @@ -62,7 +62,6 @@ export function createVegaRequestHandler( const esQueryConfigs = esQuery.getEsQueryConfig(uiSettings); const filtersDsl = esQuery.buildEsQuery(undefined, query, filters, esQueryConfigs); - // @ts-ignore const { VegaParser } = await import('./data_model/vega_parser'); const vp = new VegaParser(visParams.spec, searchAPI, timeCache, filtersDsl, serviceSettings);