mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
parent
e7d3e95c7a
commit
4473f1e547
157 changed files with 518 additions and 574 deletions
|
@ -32,7 +32,7 @@
|
|||
"statusPage": "src/legacy/core_plugins/status_page",
|
||||
"telemetry": "src/legacy/core_plugins/telemetry",
|
||||
"tileMap": "src/legacy/core_plugins/tile_map",
|
||||
"timelion": ["src/legacy/core_plugins/timelion", "src/legacy/core_plugins/vis_type_timelion"],
|
||||
"timelion": ["src/legacy/core_plugins/timelion", "src/legacy/core_plugins/vis_type_timelion", "src/plugins/timelion"],
|
||||
"uiActions": "src/plugins/ui_actions",
|
||||
"visTypeMarkdown": "src/legacy/core_plugins/vis_type_markdown",
|
||||
"visTypeMetric": "src/legacy/core_plugins/vis_type_metric",
|
||||
|
|
|
@ -21,10 +21,7 @@ import { resolve } from 'path';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { Legacy } from 'kibana';
|
||||
import { LegacyPluginApi, LegacyPluginInitializer } from 'src/legacy/plugin_discovery/types';
|
||||
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../core/utils';
|
||||
import { plugin } from './server';
|
||||
import { CustomCoreSetup } from './server/plugin';
|
||||
|
||||
const experimentalLabel = i18n.translate('timelion.uiSettings.experimentalLabel', {
|
||||
defaultMessage: 'experimental',
|
||||
|
@ -195,12 +192,6 @@ const timelionPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPl
|
|||
},
|
||||
},
|
||||
},
|
||||
init: (server: Legacy.Server) => {
|
||||
const initializerContext = {} as PluginInitializerContext;
|
||||
const core = { http: { server } } as CoreSetup & CustomCoreSetup;
|
||||
|
||||
plugin(initializerContext).setup(core);
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import PEG from 'pegjs';
|
||||
import grammar from 'raw-loader!../../../vis_type_timelion/public/chain.peg';
|
||||
import grammar from 'raw-loader!../../../../../plugins/timelion/common/chain.peg';
|
||||
import timelionExpressionInputTemplate from './timelion_expression_input.html';
|
||||
import {
|
||||
SUGGESTION_TYPE,
|
||||
|
|
|
@ -1,65 +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.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../calculate_interval`);
|
||||
const moment = require('moment');
|
||||
const expect = require('chai').expect;
|
||||
|
||||
const from = (count, unit) =>
|
||||
moment()
|
||||
.subtract(count, unit)
|
||||
.valueOf();
|
||||
const to = moment().valueOf();
|
||||
const size = 200;
|
||||
const min = '1ms';
|
||||
|
||||
describe(filename, () => {
|
||||
it('Exports a function', () => {
|
||||
expect(fn).to.be.a('function');
|
||||
});
|
||||
|
||||
it('Only calculates when interval = auto', () => {
|
||||
const partialFn = interval => fn(from(1, 'y'), to, size, interval, min);
|
||||
expect(partialFn('1ms')).to.equal('1ms');
|
||||
expect(partialFn('bag_of_beans')).to.equal('bag_of_beans');
|
||||
expect(partialFn('auto')).to.not.equal('auto');
|
||||
});
|
||||
|
||||
it('Calculates nice round intervals', () => {
|
||||
const partialFn = (count, unit) => fn(from(count, unit), to, size, 'auto', min);
|
||||
expect(partialFn(15, 'm')).to.equal('1s');
|
||||
expect(partialFn(1, 'h')).to.equal('30s');
|
||||
expect(partialFn(3, 'd')).to.equal('30m');
|
||||
expect(partialFn(1, 'w')).to.equal('1h');
|
||||
expect(partialFn(1, 'y')).to.equal('24h');
|
||||
expect(partialFn(100, 'y')).to.equal('1y');
|
||||
});
|
||||
|
||||
it('Does not calculate an interval lower than the minimum', () => {
|
||||
const partialFn = (count, unit) => fn(from(count, unit), to, size, 'auto', '1m');
|
||||
expect(partialFn(5, 's')).to.equal('1m');
|
||||
expect(partialFn(15, 'm')).to.equal('1m');
|
||||
expect(partialFn(1, 'h')).to.equal('1m');
|
||||
expect(partialFn(3, 'd')).to.equal('30m');
|
||||
expect(partialFn(1, 'w')).to.equal('1h');
|
||||
expect(partialFn(1, 'y')).to.equal('24h');
|
||||
expect(partialFn(100, 'y')).to.equal('1y');
|
||||
});
|
||||
});
|
|
@ -24,7 +24,11 @@ import moment from 'moment-timezone';
|
|||
import { timefilter } from 'ui/timefilter';
|
||||
// @ts-ignore
|
||||
import observeResize from '../../lib/observe_resize';
|
||||
import { calculateInterval, DEFAULT_TIME_FORMAT } from '../../../../vis_type_timelion/common/lib';
|
||||
import {
|
||||
calculateInterval,
|
||||
DEFAULT_TIME_FORMAT,
|
||||
// @ts-ignore
|
||||
} from '../../../../../../plugins/timelion/common/lib';
|
||||
import { tickFormatters } from '../../../../vis_type_timelion/public/helpers/tick_formatters';
|
||||
import { TimelionVisualizationDependencies } from '../../plugin';
|
||||
import { xaxisFormatterProvider } from '../../../../vis_type_timelion/public/helpers/xaxis_formatter';
|
||||
|
|
|
@ -1,62 +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 { Legacy } from 'kibana';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { PluginInitializerContext, CoreSetup } from 'kibana/server';
|
||||
|
||||
import loadFunctions, { LoadFunctions } from './lib/load_functions';
|
||||
import { initRoutes } from './routes';
|
||||
|
||||
function getFunction(functions: LoadFunctions, name: string) {
|
||||
if (functions[name]) {
|
||||
return functions[name];
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
i18n.translate('timelion.noFunctionErrorMessage', {
|
||||
defaultMessage: 'No such function: {name}',
|
||||
values: { name },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Remove as CoreSetup is completed.
|
||||
export interface CustomCoreSetup {
|
||||
http: {
|
||||
server: Legacy.Server;
|
||||
};
|
||||
}
|
||||
|
||||
export class TimelionServerPlugin {
|
||||
public initializerContext: PluginInitializerContext;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {
|
||||
this.initializerContext = initializerContext;
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup & CustomCoreSetup) {
|
||||
const { server } = core.http;
|
||||
const functions = loadFunctions('series_functions');
|
||||
|
||||
server.expose('functions', functions);
|
||||
server.expose('getFunction', (name: string) => getFunction(functions, name));
|
||||
|
||||
initRoutes(server);
|
||||
}
|
||||
}
|
|
@ -1,32 +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 { Legacy } from 'kibana';
|
||||
|
||||
// @ts-ignore
|
||||
import { runRoute } from './run';
|
||||
// @ts-ignore
|
||||
import { functionsRoute } from './functions';
|
||||
// @ts-ignore
|
||||
import { validateEsRoute } from './validate_es';
|
||||
|
||||
export function initRoutes(server: Legacy.Server) {
|
||||
runRoute(server);
|
||||
functionsRoute(server);
|
||||
validateEsRoute(server);
|
||||
}
|
|
@ -1,125 +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 Joi from 'joi';
|
||||
import Bluebird from 'bluebird';
|
||||
import _ from 'lodash';
|
||||
import { Legacy } from 'kibana';
|
||||
// @ts-ignore
|
||||
import chainRunnerFn from '../handlers/chain_runner.js';
|
||||
// @ts-ignore
|
||||
import getNamespacesSettings from '../lib/get_namespaced_settings';
|
||||
// @ts-ignore
|
||||
import getTlConfig from '../handlers/lib/tl_config';
|
||||
|
||||
const timelionDefaults = getNamespacesSettings();
|
||||
|
||||
export interface TimelionRequestQuery {
|
||||
payload: {
|
||||
sheet: string[];
|
||||
extended?: {
|
||||
es: {
|
||||
filter: {
|
||||
bool: {
|
||||
filter: string[] | object;
|
||||
must: string[];
|
||||
should: string[];
|
||||
must_not: string[];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
time?: {
|
||||
from?: string;
|
||||
interval: string;
|
||||
timezone: string;
|
||||
to?: string;
|
||||
};
|
||||
}
|
||||
|
||||
function formatErrorResponse(e: Error, h: Legacy.ResponseToolkit) {
|
||||
return h
|
||||
.response({
|
||||
title: e.toString(),
|
||||
message: e.toString(),
|
||||
})
|
||||
.code(500);
|
||||
}
|
||||
|
||||
const requestPayload = {
|
||||
payload: Joi.object({
|
||||
sheet: Joi.array()
|
||||
.items(Joi.string())
|
||||
.required(),
|
||||
extended: Joi.object({
|
||||
es: Joi.object({
|
||||
filter: Joi.object({
|
||||
bool: Joi.object({
|
||||
filter: Joi.array().allow(null),
|
||||
must: Joi.array().allow(null),
|
||||
should: Joi.array().allow(null),
|
||||
must_not: Joi.array().allow(null),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}).optional(),
|
||||
time: Joi.object({
|
||||
from: Joi.string(),
|
||||
interval: Joi.string().required(),
|
||||
timezone: Joi.string().required(),
|
||||
to: Joi.string(),
|
||||
}).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export function runRoute(server: Legacy.Server) {
|
||||
server.route({
|
||||
method: 'POST',
|
||||
path: '/api/timelion/run',
|
||||
options: {
|
||||
validate: requestPayload,
|
||||
},
|
||||
handler: async (request: Legacy.Request & TimelionRequestQuery, h: Legacy.ResponseToolkit) => {
|
||||
try {
|
||||
const uiSettings = await request.getUiSettingsService().getAll();
|
||||
|
||||
const tlConfig = getTlConfig({
|
||||
server,
|
||||
request,
|
||||
settings: _.defaults(uiSettings, timelionDefaults), // Just in case they delete some setting.
|
||||
});
|
||||
const chainRunner = chainRunnerFn(tlConfig);
|
||||
const sheet = await Bluebird.all(chainRunner.processRequest(request.payload));
|
||||
|
||||
return {
|
||||
sheet,
|
||||
stats: chainRunner.getStats(),
|
||||
};
|
||||
} catch (err) {
|
||||
server.log(['timelion', 'error'], `${err.toString()}: ${err.stack}`);
|
||||
// TODO Maybe we should just replace everywhere we throw with Boom? Probably.
|
||||
if (err.isBoom) {
|
||||
return err;
|
||||
} else {
|
||||
return formatErrorResponse(err, h);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
|
@ -24,7 +24,7 @@ import { debounce, compact, get, each, cloneDeep, last, map } from 'lodash';
|
|||
|
||||
import { useKibana } from '../../../../../plugins/kibana_react/public';
|
||||
import '../flot';
|
||||
import { DEFAULT_TIME_FORMAT } from '../../common/lib';
|
||||
import { DEFAULT_TIME_FORMAT } from '../../../../../plugins/timelion/common/lib';
|
||||
|
||||
import {
|
||||
buildSeriesData,
|
||||
|
|
|
@ -24,8 +24,11 @@ import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
|
|||
|
||||
import { CodeEditor, useKibana } from '../../../../../plugins/kibana_react/public';
|
||||
import { suggest, getSuggestion } from './timelion_expression_input_helpers';
|
||||
import { ITimelionFunction, TimelionFunctionArgs } from '../../common/types';
|
||||
import { getArgValueSuggestions } from '../helpers/arg_value_suggestions';
|
||||
import {
|
||||
ITimelionFunction,
|
||||
TimelionFunctionArgs,
|
||||
} from '../../../../../plugins/timelion/common/types';
|
||||
|
||||
const LANGUAGE_ID = 'timelion_expression';
|
||||
monacoEditor.languages.register({ id: LANGUAGE_ID });
|
||||
|
|
|
@ -22,7 +22,7 @@ import { getArgValueSuggestions } from '../helpers/arg_value_suggestions';
|
|||
import { setIndexPatterns, setSavedObjectsClient } from '../helpers/plugin_services';
|
||||
import { IndexPatterns } from 'src/plugins/data/public';
|
||||
import { SavedObjectsClient } from 'kibana/public';
|
||||
import { ITimelionFunction } from '../../common/types';
|
||||
import { ITimelionFunction } from '../../../../../plugins/timelion/common/types';
|
||||
|
||||
describe('Timelion expression suggestions', () => {
|
||||
setIndexPatterns({} as IndexPatterns);
|
||||
|
|
|
@ -18,16 +18,19 @@
|
|||
*/
|
||||
|
||||
import { get, startsWith } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Parser } from 'pegjs';
|
||||
|
||||
// @ts-ignore
|
||||
import { parse } from '../_generated_/chain';
|
||||
|
||||
import { ITimelionFunction, TimelionFunctionArgs } from '../../common/types';
|
||||
import { ArgValueSuggestions, FunctionArg, Location } from '../helpers/arg_value_suggestions';
|
||||
import {
|
||||
ITimelionFunction,
|
||||
TimelionFunctionArgs,
|
||||
} from '../../../../../plugins/timelion/common/types';
|
||||
|
||||
export enum SUGGESTION_TYPE {
|
||||
ARGUMENTS = 'arguments',
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { TimelionFunctionArgs } from '../../common/types';
|
||||
import { getIndexPatterns, getSavedObjectsClient } from './plugin_services';
|
||||
import { TimelionFunctionArgs } from '../../../../../plugins/timelion/common/types';
|
||||
|
||||
export interface Location {
|
||||
min: number;
|
||||
|
|
|
@ -23,7 +23,7 @@ import moment, { Moment } from 'moment-timezone';
|
|||
import { TimefilterContract } from 'src/plugins/data/public';
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
|
||||
import { calculateInterval } from '../../common/lib';
|
||||
import { calculateInterval } from '../../../../../plugins/timelion/common/lib';
|
||||
import { xaxisFormatterProvider } from './xaxis_formatter';
|
||||
import { Series } from './timelion_request_handler';
|
||||
|
||||
|
|
66
src/plugins/timelion/common/lib/calculate_interval.test.ts
Normal file
66
src/plugins/timelion/common/lib/calculate_interval.test.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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 { calculateInterval as fn } from './calculate_interval';
|
||||
|
||||
import moment, { unitOfTime } from 'moment';
|
||||
|
||||
const from = (count: number, unit: unitOfTime.DurationConstructor) =>
|
||||
moment()
|
||||
.subtract(count, unit)
|
||||
.valueOf();
|
||||
const to = moment().valueOf();
|
||||
const size = 200;
|
||||
const min = '1ms';
|
||||
|
||||
describe('calculate_interval', () => {
|
||||
it('Exports a function', () => {
|
||||
expect(typeof fn).toBe('function');
|
||||
});
|
||||
|
||||
it('Only calculates when interval = auto', () => {
|
||||
const partialFn = (interval: string) => fn(from(1, 'y'), to, size, interval, min);
|
||||
expect(partialFn('1ms')).toEqual('1ms');
|
||||
expect(partialFn('bag_of_beans')).toEqual('bag_of_beans');
|
||||
expect(partialFn('auto')).not.toEqual('auto');
|
||||
});
|
||||
|
||||
it('Calculates nice round intervals', () => {
|
||||
const partialFn = (count: number, unit: unitOfTime.DurationConstructor) =>
|
||||
fn(from(count, unit), to, size, 'auto', min);
|
||||
expect(partialFn(15, 'm')).toEqual('1s');
|
||||
expect(partialFn(1, 'h')).toEqual('30s');
|
||||
expect(partialFn(3, 'd')).toEqual('30m');
|
||||
expect(partialFn(1, 'w')).toEqual('1h');
|
||||
expect(partialFn(1, 'y')).toEqual('24h');
|
||||
expect(partialFn(100, 'y')).toEqual('1y');
|
||||
});
|
||||
|
||||
it('Does not calculate an interval lower than the minimum', () => {
|
||||
const partialFn = (count: number, unit: unitOfTime.DurationConstructor) =>
|
||||
fn(from(count, unit), to, size, 'auto', '1m');
|
||||
expect(partialFn(5, 's')).toEqual('1m');
|
||||
expect(partialFn(15, 'm')).toEqual('1m');
|
||||
expect(partialFn(1, 'h')).toEqual('1m');
|
||||
expect(partialFn(3, 'd')).toEqual('30m');
|
||||
expect(partialFn(1, 'w')).toEqual('1h');
|
||||
expect(partialFn(1, 'y')).toEqual('24h');
|
||||
expect(partialFn(100, 'y')).toEqual('1y');
|
||||
});
|
||||
});
|
|
@ -17,10 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { toMS } from './to_milliseconds';
|
||||
|
||||
// Totally cribbed this from Kibana 3.
|
||||
// I bet there's something similar in the Kibana 4 code. Somewhere. Somehow.
|
||||
import { toMS } from './to_milliseconds';
|
||||
|
||||
function roundInterval(interval: number) {
|
||||
switch (true) {
|
||||
case interval <= 500: // <= 0.5s
|
|
@ -22,6 +22,7 @@ import { schema } from '@kbn/config-schema';
|
|||
export const ConfigSchema = schema.object(
|
||||
{
|
||||
ui: schema.object({ enabled: schema.boolean({ defaultValue: false }) }),
|
||||
graphiteUrls: schema.maybe(schema.arrayOf(schema.string())),
|
||||
},
|
||||
// This option should be removed as soon as we entirely migrate config from legacy Timelion plugin.
|
||||
{ allowUnknowns: true }
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../average`);
|
||||
import fn from './average';
|
||||
import moment from 'moment';
|
||||
const expect = require('chai').expect;
|
||||
import _ from 'lodash';
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../carry`);
|
||||
import fn from './carry';
|
||||
import moment from 'moment';
|
||||
const expect = require('chai').expect;
|
||||
import _ from 'lodash';
|
|
@ -26,7 +26,7 @@ import parseSheet from './lib/parse_sheet.js';
|
|||
import repositionArguments from './lib/reposition_arguments.js';
|
||||
import indexArguments from './lib/index_arguments.js';
|
||||
import validateTime from './lib/validate_time.js';
|
||||
import { calculateInterval } from '../../../vis_type_timelion/common/lib';
|
||||
import { calculateInterval } from '../../common/lib';
|
||||
|
||||
export default function chainRunner(tlConfig) {
|
||||
const preprocessChain = require('./lib/preprocess_chain')(tlConfig);
|
||||
|
@ -41,7 +41,7 @@ export default function chainRunner(tlConfig) {
|
|||
|
||||
// Invokes a modifier function, resolving arguments into series as needed
|
||||
function invoke(fnName, args) {
|
||||
const functionDef = tlConfig.server.plugins.timelion.getFunction(fnName);
|
||||
const functionDef = tlConfig.getFunction(fnName);
|
||||
|
||||
function resolveArgument(item) {
|
||||
if (Array.isArray(item)) {
|
||||
|
@ -51,7 +51,7 @@ export default function chainRunner(tlConfig) {
|
|||
if (_.isObject(item)) {
|
||||
switch (item.type) {
|
||||
case 'function': {
|
||||
const itemFunctionDef = tlConfig.server.plugins.timelion.getFunction(item.function);
|
||||
const itemFunctionDef = tlConfig.getFunction(item.function);
|
||||
if (itemFunctionDef.cacheKey && queryCache[itemFunctionDef.cacheKey(item)]) {
|
||||
stats.queryCount++;
|
||||
return Bluebird.resolve(_.cloneDeep(queryCache[itemFunctionDef.cacheKey(item)]));
|
||||
|
@ -168,7 +168,7 @@ export default function chainRunner(tlConfig) {
|
|||
stats.queryTime = new Date().getTime();
|
||||
|
||||
_.each(queries, function(query, i) {
|
||||
const functionDef = tlConfig.server.plugins.timelion.getFunction(query.function);
|
||||
const functionDef = tlConfig.getFunction(query.function);
|
||||
const resolvedDatasource = resolvedDatasources[i];
|
||||
|
||||
if (resolvedDatasource.isRejected()) {
|
|
@ -21,10 +21,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
const grammar = fs.readFileSync(
|
||||
path.resolve(__dirname, '../../../../vis_type_timelion/public/chain.peg'),
|
||||
'utf8'
|
||||
);
|
||||
const grammar = fs.readFileSync(path.resolve(__dirname, '../../../common/chain.peg'), 'utf8');
|
||||
import PEG from 'pegjs';
|
||||
const Parser = PEG.generate(grammar);
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const parseSheet = require('../lib/parse_sheet');
|
||||
const parseSheet = require('./parse_sheet');
|
||||
|
||||
const expect = require('chai').expect;
|
||||
|
|
@ -24,7 +24,7 @@ export default function preProcessChainFn(tlConfig) {
|
|||
queries = queries || {};
|
||||
function validateAndStore(item) {
|
||||
if (_.isObject(item) && item.type === 'function') {
|
||||
const functionDef = tlConfig.server.plugins.timelion.getFunction(item.function);
|
||||
const functionDef = tlConfig.getFunction(item.function);
|
||||
|
||||
if (functionDef.datasource) {
|
||||
queries[functionDef.cacheKey(item)] = item;
|
|
@ -20,7 +20,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import moment from 'moment';
|
||||
|
||||
import { toMS } from '../../../../vis_type_timelion/common/lib';
|
||||
import { toMS } from '../../../common/lib/to_milliseconds';
|
||||
|
||||
export default function validateTime(time, tlConfig) {
|
||||
const span = moment.duration(moment(time.to).diff(moment(time.from))).asMilliseconds();
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import unzipPairs from './unzipPairs.js';
|
||||
import unzipPairs from './unzip_pairs.js';
|
||||
|
||||
export default function asSorted(timeValObject, fn) {
|
||||
const data = unzipPairs(timeValObject);
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TimelionFunctionArgs } from '../../../../vis_type_timelion/common/types';
|
||||
import { TimelionFunctionArgs } from '../../../common/types';
|
||||
|
||||
export interface TimelionFunctionInterface extends TimelionFunctionConfig {
|
||||
chainable: boolean;
|
|
@ -18,8 +18,28 @@
|
|||
*/
|
||||
|
||||
import { PluginInitializerContext } from 'kibana/server';
|
||||
import { TimelionServerPlugin as Plugin } from './plugin';
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import { ConfigSchema } from '../config';
|
||||
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new Plugin(initializerContext);
|
||||
export class ConfigManager {
|
||||
private esShardTimeout: number = 0;
|
||||
private graphiteUrls: string[] = [];
|
||||
|
||||
constructor(config: PluginInitializerContext['config']) {
|
||||
config.create<TypeOf<typeof ConfigSchema>>().subscribe(configUpdate => {
|
||||
this.graphiteUrls = configUpdate.graphiteUrls || [];
|
||||
});
|
||||
|
||||
config.legacy.globalConfig$.subscribe(configUpdate => {
|
||||
this.esShardTimeout = configUpdate.elasticsearch.shardTimeout.asMilliseconds();
|
||||
});
|
||||
}
|
||||
|
||||
getEsShardTimeout() {
|
||||
return this.esShardTimeout;
|
||||
}
|
||||
|
||||
getGraphiteUrls() {
|
||||
return this.graphiteUrls;
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import configFile from '../../timelion.json';
|
||||
import configFile from '../timelion.json';
|
||||
|
||||
export default function() {
|
||||
function flattenWith(dot, nestedObj, flattenArrays) {
|
|
@ -28,18 +28,18 @@ export default function(directory) {
|
|||
}
|
||||
|
||||
// Get a list of all files and use the filename as the object key
|
||||
const files = _.map(glob.sync(path.resolve(__dirname, '../' + directory + '/*.js')), function(
|
||||
file
|
||||
) {
|
||||
const name = file.substring(file.lastIndexOf('/') + 1, file.lastIndexOf('.'));
|
||||
return getTuple(directory, name);
|
||||
});
|
||||
const files = _.map(
|
||||
glob
|
||||
.sync(path.resolve(__dirname, '../' + directory + '/*.js'))
|
||||
.filter(filename => !filename.includes('.test')),
|
||||
function(file) {
|
||||
const name = file.substring(file.lastIndexOf('/') + 1, file.lastIndexOf('.'));
|
||||
return getTuple(directory, name);
|
||||
}
|
||||
);
|
||||
|
||||
// Get a list of all directories with an index.js, use the directory name as the key in the object
|
||||
const directories = _.chain(glob.sync(path.resolve(__dirname, '../' + directory + '/*/index.js')))
|
||||
.filter(function(file) {
|
||||
return file.match(/__test__/) == null;
|
||||
})
|
||||
.map(function(file) {
|
||||
const parts = file.split('/');
|
||||
const name = parts[parts.length - 2];
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../load_functions`);
|
||||
const fn = require(`src/plugins/timelion/server/lib/load_functions`);
|
||||
|
||||
const expect = require('chai').expect;
|
||||
|
|
@ -17,11 +17,21 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import { PluginInitializerContext, RecursiveReadonly } from '../../../../src/core/server';
|
||||
import {
|
||||
CoreSetup,
|
||||
PluginInitializerContext,
|
||||
RecursiveReadonly,
|
||||
} from '../../../../src/core/server';
|
||||
import { deepFreeze } from '../../../../src/core/utils';
|
||||
import { ConfigSchema } from './config';
|
||||
import loadFunctions from './lib/load_functions';
|
||||
import { functionsRoute } from './routes/functions';
|
||||
import { validateEsRoute } from './routes/validate_es';
|
||||
import { runRoute } from './routes/run';
|
||||
import { ConfigManager } from './lib/config_manager';
|
||||
|
||||
/**
|
||||
* Describes public Timelion plugin contract returned at the `setup` stage.
|
||||
|
@ -36,12 +46,44 @@ export interface PluginSetupContract {
|
|||
export class Plugin {
|
||||
constructor(private readonly initializerContext: PluginInitializerContext) {}
|
||||
|
||||
public async setup(): Promise<RecursiveReadonly<PluginSetupContract>> {
|
||||
public async setup(core: CoreSetup): Promise<RecursiveReadonly<PluginSetupContract>> {
|
||||
const config = await this.initializerContext.config
|
||||
.create<TypeOf<typeof ConfigSchema>>()
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
const configManager = new ConfigManager(this.initializerContext.config);
|
||||
|
||||
const functions = loadFunctions('series_functions');
|
||||
|
||||
const getFunction = (name: string) => {
|
||||
if (functions[name]) {
|
||||
return functions[name];
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
i18n.translate('timelion.noFunctionErrorMessage', {
|
||||
defaultMessage: 'No such function: {name}',
|
||||
values: { name },
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const logger = this.initializerContext.logger.get('timelion');
|
||||
|
||||
const router = core.http.createRouter();
|
||||
|
||||
const deps = {
|
||||
configManager,
|
||||
functions,
|
||||
getFunction,
|
||||
logger,
|
||||
};
|
||||
|
||||
functionsRoute(router, deps);
|
||||
runRoute(router, deps);
|
||||
validateEsRoute(router);
|
||||
|
||||
return deepFreeze({ uiEnabled: config.ui.enabled });
|
||||
}
|
||||
|
||||
|
|
|
@ -18,18 +18,22 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { IRouter } from 'kibana/server';
|
||||
import { LoadFunctions } from '../lib/load_functions';
|
||||
|
||||
export function functionsRoute(server) {
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: '/api/timelion/functions',
|
||||
handler: () => {
|
||||
const functionArray = _.map(server.plugins.timelion.functions, function(val, key) {
|
||||
export function functionsRoute(router: IRouter, { functions }: { functions: LoadFunctions }) {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/timelion/functions',
|
||||
validate: false,
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const functionArray = _.map(functions, function(val, key) {
|
||||
// TODO: This won't work on frozen objects, it should be removed when everything is converted to datasources and chainables
|
||||
return _.extend({}, val, { name: key });
|
||||
});
|
||||
|
||||
return _.sortBy(functionArray, 'name');
|
||||
},
|
||||
});
|
||||
return response.ok({ body: _.sortBy(functionArray, 'name') });
|
||||
}
|
||||
);
|
||||
}
|
144
src/plugins/timelion/server/routes/run.ts
Normal file
144
src/plugins/timelion/server/routes/run.ts
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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 { IRouter, Logger } from 'kibana/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import Bluebird from 'bluebird';
|
||||
import _ from 'lodash';
|
||||
// @ts-ignore
|
||||
import chainRunnerFn from '../handlers/chain_runner.js';
|
||||
// @ts-ignore
|
||||
import getNamespacesSettings from '../lib/get_namespaced_settings';
|
||||
// @ts-ignore
|
||||
import getTlConfig from '../handlers/lib/tl_config';
|
||||
import { TimelionFunctionInterface } from '../types';
|
||||
import { ConfigManager } from '../lib/config_manager';
|
||||
|
||||
const timelionDefaults = getNamespacesSettings();
|
||||
|
||||
export interface TimelionRequestQuery {
|
||||
payload: {
|
||||
sheet: string[];
|
||||
extended?: {
|
||||
es: {
|
||||
filter: {
|
||||
bool: {
|
||||
filter: string[] | object;
|
||||
must: string[];
|
||||
should: string[];
|
||||
must_not: string[];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
time?: {
|
||||
from?: string;
|
||||
interval: string;
|
||||
timezone: string;
|
||||
to?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function runRoute(
|
||||
router: IRouter,
|
||||
{
|
||||
logger,
|
||||
getFunction,
|
||||
configManager,
|
||||
}: {
|
||||
logger: Logger;
|
||||
getFunction: (name: string) => TimelionFunctionInterface;
|
||||
configManager: ConfigManager;
|
||||
}
|
||||
) {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/timelion/run',
|
||||
validate: {
|
||||
body: schema.object({
|
||||
sheet: schema.arrayOf(schema.string()),
|
||||
extended: schema.maybe(
|
||||
schema.object({
|
||||
es: schema.object({
|
||||
filter: schema.object({
|
||||
bool: schema.object({
|
||||
filter: schema.maybe(
|
||||
schema.arrayOf(schema.object({}, { allowUnknowns: true }))
|
||||
),
|
||||
must: schema.maybe(schema.arrayOf(schema.object({}, { allowUnknowns: true }))),
|
||||
should: schema.maybe(
|
||||
schema.arrayOf(schema.object({}, { allowUnknowns: true }))
|
||||
),
|
||||
must_not: schema.maybe(
|
||||
schema.arrayOf(schema.object({}, { allowUnknowns: true }))
|
||||
),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
),
|
||||
time: schema.maybe(
|
||||
schema.object({
|
||||
from: schema.maybe(schema.string()),
|
||||
interval: schema.string(),
|
||||
timezone: schema.string(),
|
||||
to: schema.maybe(schema.string()),
|
||||
})
|
||||
),
|
||||
}),
|
||||
},
|
||||
},
|
||||
router.handleLegacyErrors(async (context, request, response) => {
|
||||
try {
|
||||
const uiSettings = await context.core.uiSettings.client.getAll();
|
||||
|
||||
const tlConfig = getTlConfig({
|
||||
request,
|
||||
settings: _.defaults(uiSettings, timelionDefaults), // Just in case they delete some setting.
|
||||
getFunction,
|
||||
allowedGraphiteUrls: configManager.getGraphiteUrls(),
|
||||
esShardTimeout: configManager.getEsShardTimeout(),
|
||||
savedObjectsClient: context.core.savedObjects.client,
|
||||
esDataClient: () => context.core.elasticsearch.dataClient,
|
||||
});
|
||||
const chainRunner = chainRunnerFn(tlConfig);
|
||||
const sheet = await Bluebird.all(chainRunner.processRequest(request.body));
|
||||
|
||||
return response.ok({
|
||||
body: {
|
||||
sheet,
|
||||
stats: chainRunner.getStats(),
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`${err.toString()}: ${err.stack}`);
|
||||
// TODO Maybe we should just replace everywhere we throw with Boom? Probably.
|
||||
if (err.isBoom) {
|
||||
throw err;
|
||||
} else {
|
||||
return response.internalError({
|
||||
body: {
|
||||
message: err.toString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
|
@ -18,15 +18,18 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { IRouter } from 'kibana/server';
|
||||
|
||||
export function validateEsRoute(server) {
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: '/api/timelion/validate/es',
|
||||
handler: async function(request) {
|
||||
const uiSettings = await request.getUiSettingsService().getAll();
|
||||
export function validateEsRoute(router: IRouter) {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/timelion/validate/es',
|
||||
validate: false,
|
||||
},
|
||||
async function(context, request, response) {
|
||||
const uiSettings = await context.core.uiSettings.client.getAll();
|
||||
|
||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
|
||||
const { callAsCurrentUser } = context.core.elasticsearch.dataClient;
|
||||
|
||||
const timefield = uiSettings['timelion:es.timefield'];
|
||||
|
||||
|
@ -51,24 +54,28 @@ export function validateEsRoute(server) {
|
|||
|
||||
let resp = {};
|
||||
try {
|
||||
resp = await callWithRequest(request, 'search', body);
|
||||
resp = await callAsCurrentUser('search', body);
|
||||
} catch (errResp) {
|
||||
resp = errResp;
|
||||
}
|
||||
|
||||
if (_.has(resp, 'aggregations.maxAgg.value') && _.has(resp, 'aggregations.minAgg.value')) {
|
||||
return {
|
||||
ok: true,
|
||||
field: timefield,
|
||||
min: _.get(resp, 'aggregations.minAgg.value'),
|
||||
max: _.get(resp, 'aggregations.maxAgg.value'),
|
||||
};
|
||||
return response.ok({
|
||||
body: {
|
||||
ok: true,
|
||||
field: timefield,
|
||||
min: _.get(resp, 'aggregations.minAgg.value'),
|
||||
max: _.get(resp, 'aggregations.maxAgg.value'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
resp: resp,
|
||||
};
|
||||
},
|
||||
});
|
||||
return response.ok({
|
||||
body: {
|
||||
ok: false,
|
||||
resp,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
|
@ -17,11 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../abs`);
|
||||
import fn from './abs';
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
const seriesList = require('./fixtures/seriesList.js')();
|
||||
const seriesList = require('./fixtures/series_list.js')();
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe('abs.js', function() {
|
|
@ -17,17 +17,16 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../aggregate/index.js`);
|
||||
import fn from './index';
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
import invoke from '../helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, () => {
|
||||
describe('aggregate', () => {
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
seriesList = require('./fixtures/seriesList.js')();
|
||||
seriesList = require('../fixtures/series_list.js')();
|
||||
});
|
||||
|
||||
it('first', () => {
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../bars`);
|
||||
import fn from './bars';
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
|
@ -26,7 +26,7 @@ import invoke from './helpers/invoke_series_fn.js';
|
|||
describe('bars.js', () => {
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
seriesList = require('./fixtures/seriesList.js')();
|
||||
seriesList = require('./fixtures/series_list.js')();
|
||||
});
|
||||
|
||||
it('creates the bars property, with defaults, on all series', () => {
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../color`);
|
||||
import fn from './color';
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
|
@ -26,7 +26,7 @@ import invoke from './helpers/invoke_series_fn.js';
|
|||
describe('color.js', () => {
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
seriesList = require('./fixtures/seriesList.js')();
|
||||
seriesList = require('./fixtures/series_list.js')();
|
||||
});
|
||||
|
||||
it('sets the color, on all series', () => {
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../condition`);
|
||||
import fn from './condition';
|
||||
import moment from 'moment';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
@ -28,7 +28,7 @@ describe('condition.js', function() {
|
|||
let comparable;
|
||||
let seriesList;
|
||||
beforeEach(function() {
|
||||
seriesList = require('./fixtures/seriesList.js')();
|
||||
seriesList = require('./fixtures/series_list.js')();
|
||||
comparable = getSeriesList('', [
|
||||
[moment.utc('1980-01-01T00:00:00.000Z'), 12],
|
||||
[moment.utc('1981-01-01T00:00:00.000Z'), 33],
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../cusum`);
|
||||
import fn from './cusum';
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
|
@ -26,7 +26,7 @@ import invoke from './helpers/invoke_series_fn.js';
|
|||
describe('cusum.js', () => {
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
seriesList = require('./fixtures/seriesList.js')();
|
||||
seriesList = require('./fixtures/series_list.js')();
|
||||
});
|
||||
|
||||
it('progressively adds the numbers in the list', () => {
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../derivative`);
|
||||
import fn from './derivative';
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
|
@ -26,7 +26,7 @@ import invoke from './helpers/invoke_series_fn.js';
|
|||
describe('derivative.js', () => {
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
seriesList = require('./fixtures/seriesList.js')();
|
||||
seriesList = require('./fixtures/series_list.js')();
|
||||
});
|
||||
|
||||
it('gets the change in the set', () => {
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../divide`);
|
||||
import fn from './divide';
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
|
@ -26,7 +26,7 @@ import invoke from './helpers/invoke_series_fn.js';
|
|||
describe('divide.js', () => {
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
seriesList = require('./fixtures/seriesList.js')();
|
||||
seriesList = require('./fixtures/series_list.js')();
|
||||
});
|
||||
|
||||
it('divides by a single number', () => {
|
|
@ -17,52 +17,38 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
import es from '../es';
|
||||
import es from './index';
|
||||
|
||||
import tlConfigFn from './fixtures/tlConfig';
|
||||
import * as aggResponse from '../es/lib/agg_response_to_series_list';
|
||||
import buildRequest from '../es/lib/build_request';
|
||||
import createDateAgg from '../es/lib/create_date_agg';
|
||||
import esResponse from './fixtures/es_response';
|
||||
import tlConfigFn from '../fixtures/tl_config';
|
||||
import * as aggResponse from './lib/agg_response_to_series_list';
|
||||
import buildRequest from './lib/build_request';
|
||||
import createDateAgg from './lib/create_date_agg';
|
||||
import esResponse from '../fixtures/es_response';
|
||||
|
||||
import Bluebird from 'bluebird';
|
||||
import _ from 'lodash';
|
||||
import { expect } from 'chai';
|
||||
import sinon from 'sinon';
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
import invoke from '../helpers/invoke_series_fn.js';
|
||||
|
||||
function stubRequestAndServer(response, indexPatternSavedObjects = []) {
|
||||
return {
|
||||
server: {
|
||||
plugins: {
|
||||
elasticsearch: {
|
||||
getCluster: sinon
|
||||
.stub()
|
||||
.withArgs('data')
|
||||
.returns({
|
||||
callWithRequest: function() {
|
||||
return Bluebird.resolve(response);
|
||||
},
|
||||
}),
|
||||
},
|
||||
esDataClient: sinon.stub().returns({
|
||||
callAsCurrentUser: function() {
|
||||
return Bluebird.resolve(response);
|
||||
},
|
||||
},
|
||||
request: {
|
||||
getSavedObjectsClient: function() {
|
||||
return {
|
||||
find: function() {
|
||||
return Bluebird.resolve({
|
||||
saved_objects: indexPatternSavedObjects,
|
||||
});
|
||||
},
|
||||
};
|
||||
}),
|
||||
savedObjectsClient: {
|
||||
find: function() {
|
||||
return Bluebird.resolve({
|
||||
saved_objects: indexPatternSavedObjects,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe(filename, () => {
|
||||
describe('es', () => {
|
||||
let tlConfig;
|
||||
|
||||
describe('seriesList processor', () => {
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import Datasource from '../../lib/classes/datasource';
|
||||
|
@ -109,7 +108,7 @@ export default new Datasource('es', {
|
|||
fit: 'nearest',
|
||||
});
|
||||
|
||||
const findResp = await tlConfig.request.getSavedObjectsClient().find({
|
||||
const findResp = await tlConfig.savedObjectsClient.find({
|
||||
type: 'index-pattern',
|
||||
fields: ['title', 'fields'],
|
||||
search: `"${config.index}"`,
|
||||
|
@ -126,17 +125,12 @@ export default new Datasource('es', {
|
|||
});
|
||||
}
|
||||
|
||||
const esShardTimeout = await tlConfig.server.newPlatform.__internals.elasticsearch.legacy.config$
|
||||
.pipe(
|
||||
first(),
|
||||
map(config => config.shardTimeout.asMilliseconds())
|
||||
)
|
||||
.toPromise();
|
||||
const esShardTimeout = tlConfig.esShardTimeout;
|
||||
|
||||
const body = buildRequest(config, tlConfig, scriptedFields, esShardTimeout);
|
||||
|
||||
const { callWithRequest } = tlConfig.server.plugins.elasticsearch.getCluster('data');
|
||||
const resp = await callWithRequest(tlConfig.request, 'search', body);
|
||||
const { callAsCurrentUser: callWithRequest } = tlConfig.esDataClient();
|
||||
const resp = await callWithRequest('search', body);
|
||||
if (!resp._shards.total) {
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.esFunction.indexNotFoundErrorMessage', {
|
|
@ -17,10 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../first`);
|
||||
import fn from './first';
|
||||
|
||||
const expect = require('chai').expect;
|
||||
const seriesList = require('./fixtures/seriesList.js')();
|
||||
const seriesList = require('./fixtures/series_list.js')();
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe('first.js', function() {
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const fn = require(`../fit`);
|
||||
const fn = require(`src/plugins/timelion/server/series_functions/fit`);
|
||||
import moment from 'moment';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
|
@ -17,6 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable quotes */
|
||||
|
||||
/*
|
||||
Really didn't want to do this, but testing the agg flatten logic
|
||||
in units isn't really possible since the functions depend on each other
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import buckets from './bucketList';
|
||||
import buckets from './bucket_list';
|
||||
import getSeries from '../helpers/get_series';
|
||||
import getSeriesList from '../helpers/get_series_list';
|
||||
|
|
@ -18,48 +18,25 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { of } from 'rxjs';
|
||||
import sinon from 'sinon';
|
||||
import timelionDefaults from '../../../lib/get_namespaced_settings';
|
||||
import timelionDefaults from '../../lib/get_namespaced_settings';
|
||||
import esResponse from './es_response';
|
||||
|
||||
export default function() {
|
||||
const functions = require('../../../lib/load_functions')('series_functions');
|
||||
const kibanaServerConfigs = {
|
||||
'timelion.graphiteUrls': ['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
|
||||
};
|
||||
const server = {
|
||||
plugins: {
|
||||
timelion: {
|
||||
getFunction: name => {
|
||||
if (!functions[name]) throw new Error('No such function: ' + name);
|
||||
return functions[name];
|
||||
},
|
||||
},
|
||||
elasticsearch: {
|
||||
getCluster: sinon
|
||||
.stub()
|
||||
.withArgs('data')
|
||||
.returns({
|
||||
callWithRequest: function() {
|
||||
return Promise.resolve(esResponse);
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
newPlatform: {
|
||||
__internals: {
|
||||
elasticsearch: {
|
||||
legacy: { config$: of({ shardTimeout: moment.duration(30000) }) },
|
||||
},
|
||||
},
|
||||
},
|
||||
config: () => ({ get: key => kibanaServerConfigs[key] }),
|
||||
};
|
||||
const functions = require('../../lib/load_functions')('series_functions');
|
||||
|
||||
const tlConfig = require('../../../handlers/lib/tl_config.js')({
|
||||
server,
|
||||
request: {},
|
||||
const tlConfig = require('../../handlers/lib/tl_config.js')({
|
||||
getFunction: name => {
|
||||
if (!functions[name]) throw new Error('No such function: ' + name);
|
||||
return functions[name];
|
||||
},
|
||||
esDataClient: sinon.stub().returns({
|
||||
callAsCurrentUser: function() {
|
||||
return Promise.resolve(esResponse);
|
||||
},
|
||||
}),
|
||||
esShardTimeout: moment.duration(30000),
|
||||
allowedGraphiteUrls: ['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'],
|
||||
});
|
||||
|
||||
tlConfig.time = {
|
|
@ -46,7 +46,7 @@ export default new Datasource('graphite', {
|
|||
min: moment(tlConfig.time.from).format('HH:mm[_]YYYYMMDD'),
|
||||
max: moment(tlConfig.time.to).format('HH:mm[_]YYYYMMDD'),
|
||||
};
|
||||
const allowedUrls = tlConfig.server.config().get('timelion.graphiteUrls');
|
||||
const allowedUrls = tlConfig.allowedGraphiteUrls;
|
||||
const configuredUrl = tlConfig.settings['timelion:graphite.url'];
|
||||
if (!allowedUrls.includes(configuredUrl)) {
|
||||
throw new Error(
|
|
@ -17,12 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import proxyquire from 'proxyquire';
|
||||
import Bluebird from 'bluebird';
|
||||
const expect = require('chai').expect;
|
||||
|
||||
const graphiteResponse = function() {
|
||||
return Bluebird.resolve({
|
||||
import fn from './graphite';
|
||||
|
||||
jest.mock('node-fetch', () => () => {
|
||||
return Promise.resolve({
|
||||
json: function() {
|
||||
return [
|
||||
{
|
||||
|
@ -37,14 +37,11 @@ const graphiteResponse = function() {
|
|||
];
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = proxyquire(`../${filename}`, { 'node-fetch': graphiteResponse });
|
||||
});
|
||||
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, function() {
|
||||
describe('graphite', function() {
|
||||
it('should wrap the graphite response up in a seriesList', function() {
|
||||
return invoke(fn, []).then(function(result) {
|
||||
expect(result.output.list[0].data[0][1]).to.eql(3);
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue