Skip to content

Commit

Permalink
Convert vis_type_vega to Typescript (#68915)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashikmeerankutty authored Jul 15, 2020
1 parent 4c654c4 commit 5f6389a
Show file tree
Hide file tree
Showing 14 changed files with 511 additions and 158 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,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",
Expand Down Expand Up @@ -345,6 +345,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",
Expand Down
8 changes: 8 additions & 0 deletions renovate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,14 @@
'@types/history',
],
},
{
groupSlug: 'hjson',
groupName: 'hjson related packages',
packageNames: [
'hjson',
'@types/hjson',
],
},
{
groupSlug: 'inquirer',
groupName: 'inquirer related packages',
Expand Down
1 change: 1 addition & 0 deletions src/plugins/maps_legacy/public/map/service_settings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ export interface IServiceSettings {
getEMSHotLink(layer: FileLayer): Promise<string>;
getTMSServices(): Promise<TmsLayer[]>;
getFileLayers(): Promise<FileLayer[]>;
getUrlForRegionLayer(layer: FileLayer): Promise<string>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,28 @@
*/

import { i18n } from '@kbn/i18n';
// @ts-ignore
import { bypassExternalUrlCheck } from '../vega_view/vega_base_view';
import { IServiceSettings, FileLayer } from '../../../maps_legacy/public';
import { Data, UrlObject, Requests } 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<FileLayer[]>;

constructor(serviceSettings: IServiceSettings) {
this._serviceSettings = serviceSettings;
}

// noinspection JSMethodCanBeStatic
/**
* Update request object, expanding any context-aware keywords
*/
parseUrl(obj, url) {
parseUrl(obj: Data, url: UrlObject) {
if (typeof url.name !== 'string') {
throw new Error(
i18n.translate('visTypeVega.emsFileParser.missingNameOfFileErrorMessage', {
Expand All @@ -59,13 +65,13 @@ export class EmsFileParser {
* @param {object[]} requests each object is generated by parseUrl()
* @returns {Promise<void>}
*/
async populateData(requests) {
async populateData(requests: Requests[]) {
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', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,38 @@

import moment from 'moment';
import { i18n } from '@kbn/i18n';
import { isPlainObject, cloneDeep } from 'lodash';
import { cloneDeep, isPlainObject } from 'lodash';
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';

const TIMEFILTER = '%timefilter%';
const AUTOINTERVAL = '%autointerval%';
const MUST_CLAUSE = '%dashboard_context-must_clause%';
const FILTER_CLAUSE = '%dashboard_context-filter_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%';
const FILTER_CLAUSE: string = '%dashboard_context-filter_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, searchAPI, filters, onWarning) {
_timeCache: TimeCache;
_searchAPI: SearchAPI;
_filters: Bool;
_onWarning: (...args: string[]) => void;

constructor(
timeCache: TimeCache,
searchAPI: SearchAPI,
filters: Bool,
onWarning: (...args: string[]) => void
) {
this._timeCache = timeCache;
this._searchAPI = searchAPI;
this._filters = filters;
Expand All @@ -47,7 +61,7 @@ export class EsQueryParser {
/**
* Update request object, expanding any context-aware keywords
*/
parseUrl(dataObject, url) {
parseUrl(dataObject: Data, url: UrlObject) {
let body = url.body;
let context = url[CONTEXT];
delete url[CONTEXT];
Expand Down Expand Up @@ -167,13 +181,13 @@ 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 };
}

Expand All @@ -182,8 +196,8 @@ export class EsQueryParser {
* @param {object[]} requests each object is generated by parseUrl()
* @returns {Promise<void>}
*/
async populateData(requests) {
const esSearches = requests.map((r) => 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();
Expand All @@ -198,7 +212,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: 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
Expand Down Expand Up @@ -239,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
Expand All @@ -260,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;
}

Expand All @@ -269,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
Expand Down Expand Up @@ -302,7 +318,7 @@ export class EsQueryParser {
* @param {object} obj
* @return {object}
*/
_createRangeFilter(obj) {
_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';
Expand All @@ -320,9 +336,9 @@ export class EsQueryParser {
* @param {'min'|'max'} type
* @returns {*}
*/
_getTimeBound(opts, type) {
_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;
Expand Down Expand Up @@ -380,7 +396,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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,47 @@
* under the License.
*/

import { TimefilterContract } from '../../../data/public';
import { TimeRange } from '../../../data/common';
import { CacheBounds } from './types';

/**
* 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?: CacheBounds;
_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();
}

/**
* Get cached time range values
* @returns {{min: number, max: number}}
*/
getTimeBounds() {
getTimeBounds(): CacheBounds {
const ts = this._now();

let bounds;
let bounds: CacheBounds | null = null;
if (this._cachedBounds) {
const diff = ts - this._cacheTS;

Expand Down Expand Up @@ -76,7 +86,7 @@ export class TimeCache {
return this._cachedBounds;
}

setTimeRange(timeRange) {
setTimeRange(timeRange: TimeRange): void {
this._timeRange = timeRange;
}

Expand All @@ -85,11 +95,11 @@ export class TimeCache {
* @returns {{min: number, max: number}}
* @private
*/
_getBounds() {
const bounds = this._timefilter.calculateBounds(this._timeRange);
_getBounds(): CacheBounds {
const bounds = this._timefilter.calculateBounds(this._timeRange!);
return {
min: bounds.min.valueOf(),
max: bounds.max.valueOf(),
min: bounds.min!.valueOf(),
max: bounds.max!.valueOf(),
};
}
}
Loading

0 comments on commit 5f6389a

Please sign in to comment.