mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[codemod][prereq] Convert Functions from arrow to function (#35749)
This commit is contained in:
parent
6d45682c5c
commit
7aa698f522
64 changed files with 2639 additions and 2502 deletions
|
@ -4,9 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const browser = () => ({
|
||||
name: 'browser',
|
||||
help: 'Force the interpreter to return to the browser',
|
||||
args: {},
|
||||
fn: context => context,
|
||||
});
|
||||
export function browser() {
|
||||
return {
|
||||
name: 'browser',
|
||||
help: 'Force the interpreter to return to the browser',
|
||||
args: {},
|
||||
fn: context => context,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,27 +6,29 @@
|
|||
|
||||
const noop = () => {};
|
||||
|
||||
export const location = () => ({
|
||||
name: 'location',
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
help:
|
||||
"Use the browser's location functionality to get your current location. Usually quite slow, but fairly accurate",
|
||||
fn: () => {
|
||||
return new Promise(resolve => {
|
||||
function createLocation(geoposition) {
|
||||
const { latitude, longitude } = geoposition.coords;
|
||||
return resolve({
|
||||
type: 'datatable',
|
||||
columns: [{ name: 'latitude', type: 'number' }, { name: 'longitude', type: 'number' }],
|
||||
rows: [{ latitude, longitude }],
|
||||
export function location() {
|
||||
return {
|
||||
name: 'location',
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
help:
|
||||
"Use the browser's location functionality to get your current location. Usually quite slow, but fairly accurate",
|
||||
fn: () => {
|
||||
return new Promise(resolve => {
|
||||
function createLocation(geoposition) {
|
||||
const { latitude, longitude } = geoposition.coords;
|
||||
return resolve({
|
||||
type: 'datatable',
|
||||
columns: [{ name: 'latitude', type: 'number' }, { name: 'longitude', type: 'number' }],
|
||||
rows: [{ latitude, longitude }],
|
||||
});
|
||||
}
|
||||
return navigator.geolocation.getCurrentPosition(createLocation, noop, {
|
||||
maximumAge: 5000,
|
||||
});
|
||||
}
|
||||
return navigator.geolocation.getCurrentPosition(createLocation, noop, {
|
||||
maximumAge: 5000,
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,47 +6,49 @@
|
|||
|
||||
import { Handlebars } from '../../../common/lib/handlebars';
|
||||
|
||||
export const markdown = () => ({
|
||||
name: 'markdown',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help:
|
||||
'An element for rendering markdown text. Great for single numbers, metrics or paragraphs of text.',
|
||||
context: {
|
||||
types: ['datatable', 'null'],
|
||||
},
|
||||
args: {
|
||||
expression: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'A markdown expression. You can pass this multiple times to achieve concatenation',
|
||||
default: '""',
|
||||
multi: true,
|
||||
export function markdown() {
|
||||
return {
|
||||
name: 'markdown',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help:
|
||||
'An element for rendering markdown text. Great for single numbers, metrics or paragraphs of text.',
|
||||
context: {
|
||||
types: ['datatable', 'null'],
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: 'Font settings. Technically, you can add other styles in here as well',
|
||||
default: '{font}',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const compileFunctions = args.expression.map(str =>
|
||||
Handlebars.compile(String(str), { knownHelpersOnly: true })
|
||||
);
|
||||
const ctx = {
|
||||
columns: [],
|
||||
rows: [],
|
||||
type: null,
|
||||
...context,
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'markdown',
|
||||
value: {
|
||||
content: compileFunctions.map(fn => fn(ctx)).join(''),
|
||||
font: args.font,
|
||||
args: {
|
||||
expression: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'A markdown expression. You can pass this multiple times to achieve concatenation',
|
||||
default: '""',
|
||||
multi: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: 'Font settings. Technically, you can add other styles in here as well',
|
||||
default: '{font}',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const compileFunctions = args.expression.map(str =>
|
||||
Handlebars.compile(String(str), { knownHelpersOnly: true })
|
||||
);
|
||||
const ctx = {
|
||||
columns: [],
|
||||
rows: [],
|
||||
type: null,
|
||||
...context,
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'markdown',
|
||||
value: {
|
||||
content: compileFunctions.map(fn => fn(ctx)).join(''),
|
||||
font: args.font,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,30 +6,32 @@
|
|||
|
||||
import { parse } from 'url';
|
||||
|
||||
export const urlparam = () => ({
|
||||
name: 'urlparam',
|
||||
aliases: [],
|
||||
type: 'string',
|
||||
help:
|
||||
'Access URL parameters and use them in expressions. Eg https://localhost:5601/app/canvas?myVar=20. This will always return a string',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
args: {
|
||||
param: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'var', 'variable'],
|
||||
help: 'The URL hash parameter to access',
|
||||
multi: false,
|
||||
export function urlparam() {
|
||||
return {
|
||||
name: 'urlparam',
|
||||
aliases: [],
|
||||
type: 'string',
|
||||
help:
|
||||
'Access URL parameters and use them in expressions. Eg https://localhost:5601/app/canvas?myVar=20. This will always return a string',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
default: {
|
||||
types: ['string'],
|
||||
default: '""',
|
||||
help: 'Return this string if the url parameter is not defined',
|
||||
args: {
|
||||
param: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'var', 'variable'],
|
||||
help: 'The URL hash parameter to access',
|
||||
multi: false,
|
||||
},
|
||||
default: {
|
||||
types: ['string'],
|
||||
default: '""',
|
||||
help: 'Return this string if the url parameter is not defined',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const query = parse(window.location.href, true).query;
|
||||
return query[args.param] || args.default;
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
const query = parse(window.location.href, true).query;
|
||||
return query[args.param] || args.default;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,21 +4,23 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const all = () => ({
|
||||
name: 'all',
|
||||
type: 'boolean',
|
||||
help: 'Return true if all of the conditions are true',
|
||||
args: {
|
||||
condition: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'null'],
|
||||
required: true,
|
||||
multi: true,
|
||||
help: 'One or more conditions to check',
|
||||
export function all() {
|
||||
return {
|
||||
name: 'all',
|
||||
type: 'boolean',
|
||||
help: 'Return true if all of the conditions are true',
|
||||
args: {
|
||||
condition: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'null'],
|
||||
required: true,
|
||||
multi: true,
|
||||
help: 'One or more conditions to check',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const conditions = args.condition || [];
|
||||
return conditions.every(Boolean);
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
const conditions = args.condition || [];
|
||||
return conditions.every(Boolean);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,88 +6,90 @@
|
|||
|
||||
import { omit } from 'lodash';
|
||||
|
||||
export const alterColumn = () => ({
|
||||
name: 'alterColumn',
|
||||
type: 'datatable',
|
||||
help: 'Converts between core types, eg string, number, null, boolean, date and rename columns',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
column: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'The name of the column to alter',
|
||||
export function alterColumn() {
|
||||
return {
|
||||
name: 'alterColumn',
|
||||
type: 'datatable',
|
||||
help: 'Converts between core types, eg string, number, null, boolean, date and rename columns',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
type: {
|
||||
types: ['string'],
|
||||
help: 'The type to convert the column to. Leave blank to not change type',
|
||||
default: null,
|
||||
options: ['null', 'boolean', 'number', 'string'],
|
||||
args: {
|
||||
column: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'The name of the column to alter',
|
||||
},
|
||||
type: {
|
||||
types: ['string'],
|
||||
help: 'The type to convert the column to. Leave blank to not change type',
|
||||
default: null,
|
||||
options: ['null', 'boolean', 'number', 'string'],
|
||||
},
|
||||
name: {
|
||||
types: ['string'],
|
||||
help: 'The resultant column name. Leave blank to not rename',
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
name: {
|
||||
types: ['string'],
|
||||
help: 'The resultant column name. Leave blank to not rename',
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.column || (!args.type && !args.name)) {
|
||||
return context;
|
||||
}
|
||||
|
||||
const column = context.columns.find(col => col.name === args.column);
|
||||
if (!column) {
|
||||
throw new Error(`Column not found: '${args.column}'`);
|
||||
}
|
||||
|
||||
const name = args.name || column.name;
|
||||
const type = args.type || column.type;
|
||||
|
||||
const columns = context.columns.reduce((all, col) => {
|
||||
if (col.name !== args.name) {
|
||||
if (col.name !== column.name) {
|
||||
all.push(col);
|
||||
} else {
|
||||
all.push({ name, type });
|
||||
}
|
||||
fn: (context, args) => {
|
||||
if (!args.column || (!args.type && !args.name)) {
|
||||
return context;
|
||||
}
|
||||
return all;
|
||||
}, []);
|
||||
|
||||
let handler = val => val;
|
||||
const column = context.columns.find(col => col.name === args.column);
|
||||
if (!column) {
|
||||
throw new Error(`Column not found: '${args.column}'`);
|
||||
}
|
||||
|
||||
if (args.type) {
|
||||
handler = (function getHandler() {
|
||||
switch (type) {
|
||||
case 'string':
|
||||
if (column.type === 'date') {
|
||||
return v => new Date(v).toISOString();
|
||||
}
|
||||
return String;
|
||||
case 'number':
|
||||
return Number;
|
||||
case 'date':
|
||||
return v => new Date(v).valueOf();
|
||||
case 'boolean':
|
||||
return Boolean;
|
||||
case 'null':
|
||||
return () => null;
|
||||
default:
|
||||
throw new Error(`Cannot convert to '${type}'`);
|
||||
const name = args.name || column.name;
|
||||
const type = args.type || column.type;
|
||||
|
||||
const columns = context.columns.reduce((all, col) => {
|
||||
if (col.name !== args.name) {
|
||||
if (col.name !== column.name) {
|
||||
all.push(col);
|
||||
} else {
|
||||
all.push({ name, type });
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
return all;
|
||||
}, []);
|
||||
|
||||
const rows = context.rows.map(row => ({
|
||||
...omit(row, column.name),
|
||||
[name]: handler(row[column.name]),
|
||||
}));
|
||||
let handler = val => val;
|
||||
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows,
|
||||
};
|
||||
},
|
||||
});
|
||||
if (args.type) {
|
||||
handler = (function getHandler() {
|
||||
switch (type) {
|
||||
case 'string':
|
||||
if (column.type === 'date') {
|
||||
return v => new Date(v).toISOString();
|
||||
}
|
||||
return String;
|
||||
case 'number':
|
||||
return Number;
|
||||
case 'date':
|
||||
return v => new Date(v).valueOf();
|
||||
case 'boolean':
|
||||
return Boolean;
|
||||
case 'null':
|
||||
return () => null;
|
||||
default:
|
||||
throw new Error(`Cannot convert to '${type}'`);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
const rows = context.rows.map(row => ({
|
||||
...omit(row, column.name),
|
||||
[name]: handler(row[column.name]),
|
||||
}));
|
||||
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,21 +4,23 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const any = () => ({
|
||||
name: 'any',
|
||||
type: 'boolean',
|
||||
help: 'Return true if any of the conditions are true',
|
||||
args: {
|
||||
condition: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'null'],
|
||||
required: true,
|
||||
multi: true,
|
||||
help: 'One or more conditions to check',
|
||||
export function any() {
|
||||
return {
|
||||
name: 'any',
|
||||
type: 'boolean',
|
||||
help: 'Return true if any of the conditions are true',
|
||||
args: {
|
||||
condition: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'null'],
|
||||
required: true,
|
||||
multi: true,
|
||||
help: 'One or more conditions to check',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const conditions = args.condition || [];
|
||||
return conditions.some(Boolean);
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
const conditions = args.condition || [];
|
||||
return conditions.some(Boolean);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,35 +6,37 @@
|
|||
|
||||
import { getType } from '@kbn/interpreter/common';
|
||||
|
||||
export const asFn = () => ({
|
||||
name: 'as',
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['string', 'boolean', 'number', 'null'],
|
||||
},
|
||||
help: 'Creates a datatable with a single value',
|
||||
args: {
|
||||
name: {
|
||||
types: ['string'],
|
||||
aliases: ['_'],
|
||||
help: 'A name to give the column',
|
||||
default: 'value',
|
||||
export function asFn() {
|
||||
return {
|
||||
name: 'as',
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['string', 'boolean', 'number', 'null'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{
|
||||
name: args.name,
|
||||
type: getType(context),
|
||||
},
|
||||
],
|
||||
rows: [
|
||||
{
|
||||
[args.name]: context,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
help: 'Creates a datatable with a single value',
|
||||
args: {
|
||||
name: {
|
||||
types: ['string'],
|
||||
aliases: ['_'],
|
||||
help: 'A name to give the column',
|
||||
default: 'value',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{
|
||||
name: args.name,
|
||||
type: getType(context),
|
||||
},
|
||||
],
|
||||
rows: [
|
||||
{
|
||||
[args.name]: context,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,70 +5,72 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
export const axisConfig = () => ({
|
||||
name: 'axisConfig',
|
||||
aliases: [],
|
||||
type: 'axisConfig',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
help: 'Configure axis of a visualization',
|
||||
args: {
|
||||
show: {
|
||||
types: ['boolean'],
|
||||
help: 'Show the axis labels?',
|
||||
default: true,
|
||||
export function axisConfig() {
|
||||
return {
|
||||
name: 'axisConfig',
|
||||
aliases: [],
|
||||
type: 'axisConfig',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
position: {
|
||||
types: ['string'],
|
||||
help: 'Position of the axis labels - top, bottom, left, and right',
|
||||
options: ['top', 'bottom', 'left', 'right'],
|
||||
default: 'left',
|
||||
help: 'Configure axis of a visualization',
|
||||
args: {
|
||||
show: {
|
||||
types: ['boolean'],
|
||||
help: 'Show the axis labels?',
|
||||
default: true,
|
||||
},
|
||||
position: {
|
||||
types: ['string'],
|
||||
help: 'Position of the axis labels - top, bottom, left, and right',
|
||||
options: ['top', 'bottom', 'left', 'right'],
|
||||
default: 'left',
|
||||
},
|
||||
min: {
|
||||
types: ['number', 'date', 'string', 'null'],
|
||||
help:
|
||||
'Minimum value displayed in the axis. Must be a number or a date in ms or ISO8601 string',
|
||||
},
|
||||
max: {
|
||||
types: ['number', 'date', 'string', 'null'],
|
||||
help:
|
||||
'Maximum value displayed in the axis. Must be a number or a date in ms or ISO8601 string',
|
||||
},
|
||||
tickSize: {
|
||||
types: ['number', 'null'],
|
||||
help: 'Increment size between each tick. Use for number axes only',
|
||||
},
|
||||
},
|
||||
min: {
|
||||
types: ['number', 'date', 'string', 'null'],
|
||||
help:
|
||||
'Minimum value displayed in the axis. Must be a number or a date in ms or ISO8601 string',
|
||||
},
|
||||
max: {
|
||||
types: ['number', 'date', 'string', 'null'],
|
||||
help:
|
||||
'Maximum value displayed in the axis. Must be a number or a date in ms or ISO8601 string',
|
||||
},
|
||||
tickSize: {
|
||||
types: ['number', 'null'],
|
||||
help: 'Increment size between each tick. Use for number axes only',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const positions = ['top', 'bottom', 'left', 'right', ''];
|
||||
if (!positions.includes(args.position)) {
|
||||
throw new Error(`Invalid position: '${args.position}'`);
|
||||
}
|
||||
fn: (context, args) => {
|
||||
const positions = ['top', 'bottom', 'left', 'right', ''];
|
||||
if (!positions.includes(args.position)) {
|
||||
throw new Error(`Invalid position: '${args.position}'`);
|
||||
}
|
||||
|
||||
const min = typeof args.min === 'string' ? moment.utc(args.min).valueOf() : args.min;
|
||||
const max = typeof args.max === 'string' ? moment.utc(args.max).valueOf() : args.max;
|
||||
const min = typeof args.min === 'string' ? moment.utc(args.min).valueOf() : args.min;
|
||||
const max = typeof args.max === 'string' ? moment.utc(args.max).valueOf() : args.max;
|
||||
|
||||
if (min != null && isNaN(min)) {
|
||||
throw new Error(
|
||||
`Invalid date string: '${
|
||||
args.min
|
||||
}'. 'min' must be a number, date in ms, or ISO8601 date string`
|
||||
);
|
||||
}
|
||||
if (max != null && isNaN(max)) {
|
||||
throw new Error(
|
||||
`Invalid date string: '${
|
||||
args.max
|
||||
}'. 'max' must be a number, date in ms, or ISO8601 date string`
|
||||
);
|
||||
}
|
||||
if (min != null && isNaN(min)) {
|
||||
throw new Error(
|
||||
`Invalid date string: '${
|
||||
args.min
|
||||
}'. 'min' must be a number, date in ms, or ISO8601 date string`
|
||||
);
|
||||
}
|
||||
if (max != null && isNaN(max)) {
|
||||
throw new Error(
|
||||
`Invalid date string: '${
|
||||
args.max
|
||||
}'. 'max' must be a number, date in ms, or ISO8601 date string`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'axisConfig',
|
||||
...args,
|
||||
min,
|
||||
max,
|
||||
};
|
||||
},
|
||||
});
|
||||
return {
|
||||
type: 'axisConfig',
|
||||
...args,
|
||||
min,
|
||||
max,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,33 +4,35 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const caseFn = () => ({
|
||||
name: 'case',
|
||||
type: 'case',
|
||||
help: 'Build a case (including a condition/result) to pass to the switch function',
|
||||
args: {
|
||||
when: {
|
||||
aliases: ['_'],
|
||||
resolve: false,
|
||||
help:
|
||||
'This value is compared to the context to see if the condition is met. It is overridden by the "if" argument if both are provided.',
|
||||
export function caseFn() {
|
||||
return {
|
||||
name: 'case',
|
||||
type: 'case',
|
||||
help: 'Build a case (including a condition/result) to pass to the switch function',
|
||||
args: {
|
||||
when: {
|
||||
aliases: ['_'],
|
||||
resolve: false,
|
||||
help:
|
||||
'This value is compared to the context to see if the condition is met. It is overridden by the "if" argument if both are provided.',
|
||||
},
|
||||
if: {
|
||||
types: ['boolean'],
|
||||
help:
|
||||
'This value is used as whether or not the condition is met. It overrides the unnamed argument if both are provided.',
|
||||
},
|
||||
then: {
|
||||
resolve: false,
|
||||
help: 'The value to return if the condition is met',
|
||||
},
|
||||
},
|
||||
if: {
|
||||
types: ['boolean'],
|
||||
help:
|
||||
'This value is used as whether or not the condition is met. It overrides the unnamed argument if both are provided.',
|
||||
fn: async (context, args) => {
|
||||
const matches = await doesMatch(context, args);
|
||||
const result = matches ? await getResult(context, args) : null;
|
||||
return { type: 'case', matches, result };
|
||||
},
|
||||
then: {
|
||||
resolve: false,
|
||||
help: 'The value to return if the condition is met',
|
||||
},
|
||||
},
|
||||
fn: async (context, args) => {
|
||||
const matches = await doesMatch(context, args);
|
||||
const result = matches ? await getResult(context, args) : null;
|
||||
return { type: 'case', matches, result };
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async function doesMatch(context, args) {
|
||||
if (typeof args.if !== 'undefined') {
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const clear = () => ({
|
||||
name: 'clear',
|
||||
type: 'null',
|
||||
help: 'Clears context and returns null',
|
||||
args: {},
|
||||
fn: () => null,
|
||||
});
|
||||
export function clear() {
|
||||
return {
|
||||
name: 'clear',
|
||||
type: 'null',
|
||||
help: 'Clears context and returns null',
|
||||
args: {},
|
||||
fn: () => null,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,54 +6,56 @@
|
|||
|
||||
import { omit, pick, find } from 'lodash';
|
||||
|
||||
export const columns = () => ({
|
||||
name: 'columns',
|
||||
type: 'datatable',
|
||||
help:
|
||||
'Include or exclude columns from a data table. If you specify both, this will exclude first',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
include: {
|
||||
types: ['string'],
|
||||
help: 'A comma separated list of column names to keep in the table',
|
||||
default: null,
|
||||
export function columns() {
|
||||
return {
|
||||
name: 'columns',
|
||||
type: 'datatable',
|
||||
help:
|
||||
'Include or exclude columns from a data table. If you specify both, this will exclude first',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
exclude: {
|
||||
types: ['string'],
|
||||
help: 'A comma separated list of column names to remove from the table',
|
||||
default: null,
|
||||
args: {
|
||||
include: {
|
||||
types: ['string'],
|
||||
help: 'A comma separated list of column names to keep in the table',
|
||||
default: null,
|
||||
},
|
||||
exclude: {
|
||||
types: ['string'],
|
||||
help: 'A comma separated list of column names to remove from the table',
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const { include, exclude } = args;
|
||||
fn: (context, args) => {
|
||||
const { include, exclude } = args;
|
||||
|
||||
let result = { ...context };
|
||||
let result = { ...context };
|
||||
|
||||
if (exclude) {
|
||||
const fields = exclude.split(',').map(field => field.trim());
|
||||
const columns = result.columns.filter(col => !fields.includes(col.name));
|
||||
const rows = columns.length > 0 ? result.rows.map(row => omit(row, fields)) : [];
|
||||
if (exclude) {
|
||||
const fields = exclude.split(',').map(field => field.trim());
|
||||
const columns = result.columns.filter(col => !fields.includes(col.name));
|
||||
const rows = columns.length > 0 ? result.rows.map(row => omit(row, fields)) : [];
|
||||
|
||||
result = { ...result, rows, columns };
|
||||
}
|
||||
result = { ...result, rows, columns };
|
||||
}
|
||||
|
||||
if (include) {
|
||||
const fields = include.split(',').map(field => field.trim());
|
||||
//const columns = result.columns.filter(col => fields.includes(col.name));
|
||||
// Include columns in the order the user specified
|
||||
const columns = [];
|
||||
fields.forEach(field => {
|
||||
const column = find(result.columns, { name: field });
|
||||
if (column) {
|
||||
columns.push(column);
|
||||
}
|
||||
});
|
||||
const rows = columns.length > 0 ? result.rows.map(row => pick(row, fields)) : [];
|
||||
result = { ...result, rows, columns };
|
||||
}
|
||||
if (include) {
|
||||
const fields = include.split(',').map(field => field.trim());
|
||||
//const columns = result.columns.filter(col => fields.includes(col.name));
|
||||
// Include columns in the order the user specified
|
||||
const columns = [];
|
||||
fields.forEach(field => {
|
||||
const column = find(result.columns, { name: field });
|
||||
if (column) {
|
||||
columns.push(column);
|
||||
}
|
||||
});
|
||||
const rows = columns.length > 0 ? result.rows.map(row => pick(row, fields)) : [];
|
||||
result = { ...result, rows, columns };
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
});
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,66 +4,68 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const compare = () => ({
|
||||
name: 'compare',
|
||||
help:
|
||||
'Compare the input to something else to determine true or false. Usually used in combination with `{if}`. This only works with primitive types, such as number, string, and boolean.',
|
||||
aliases: ['condition'],
|
||||
example: 'math "random()" | compare gt this=0.5',
|
||||
type: 'boolean',
|
||||
context: {
|
||||
types: ['null', 'string', 'number', 'boolean'],
|
||||
},
|
||||
args: {
|
||||
op: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
default: 'eq',
|
||||
help:
|
||||
'The operator to use in the comparison: ' +
|
||||
' eq (equal), ne (not equal), lt (less than), gt (greater than), lte (less than equal), gte (greater than eq)',
|
||||
options: ['eq', 'ne', 'lt', 'gt', 'lte', 'gte'],
|
||||
export function compare() {
|
||||
return {
|
||||
name: 'compare',
|
||||
help:
|
||||
'Compare the input to something else to determine true or false. Usually used in combination with `{if}`. This only works with primitive types, such as number, string, and boolean.',
|
||||
aliases: ['condition'],
|
||||
example: 'math "random()" | compare gt this=0.5',
|
||||
type: 'boolean',
|
||||
context: {
|
||||
types: ['null', 'string', 'number', 'boolean'],
|
||||
},
|
||||
to: {
|
||||
aliases: ['this', 'b'],
|
||||
help: 'The value to compare the context to, usually returned by a subexpression',
|
||||
args: {
|
||||
op: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
default: 'eq',
|
||||
help:
|
||||
'The operator to use in the comparison: ' +
|
||||
' eq (equal), ne (not equal), lt (less than), gt (greater than), lte (less than equal), gte (greater than eq)',
|
||||
options: ['eq', 'ne', 'lt', 'gt', 'lte', 'gte'],
|
||||
},
|
||||
to: {
|
||||
aliases: ['this', 'b'],
|
||||
help: 'The value to compare the context to, usually returned by a subexpression',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const a = context;
|
||||
const b = args.to;
|
||||
const op = args.op;
|
||||
const typesMatch = typeof a === typeof b;
|
||||
fn: (context, args) => {
|
||||
const a = context;
|
||||
const b = args.to;
|
||||
const op = args.op;
|
||||
const typesMatch = typeof a === typeof b;
|
||||
|
||||
switch (op) {
|
||||
case 'eq':
|
||||
return a === b;
|
||||
case 'ne':
|
||||
return a !== b;
|
||||
case 'lt':
|
||||
if (typesMatch) {
|
||||
return a < b;
|
||||
}
|
||||
return false;
|
||||
case 'lte':
|
||||
if (typesMatch) {
|
||||
return a <= b;
|
||||
}
|
||||
return false;
|
||||
case 'gt':
|
||||
if (typesMatch) {
|
||||
return a > b;
|
||||
}
|
||||
return false;
|
||||
case 'gte':
|
||||
if (typesMatch) {
|
||||
return a >= b;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
throw new Error(`Invalid compare operator: '${op}'. Use eq, ne, lt, gt, lte, or gte.`);
|
||||
}
|
||||
switch (op) {
|
||||
case 'eq':
|
||||
return a === b;
|
||||
case 'ne':
|
||||
return a !== b;
|
||||
case 'lt':
|
||||
if (typesMatch) {
|
||||
return a < b;
|
||||
}
|
||||
return false;
|
||||
case 'lte':
|
||||
if (typesMatch) {
|
||||
return a <= b;
|
||||
}
|
||||
return false;
|
||||
case 'gt':
|
||||
if (typesMatch) {
|
||||
return a > b;
|
||||
}
|
||||
return false;
|
||||
case 'gte':
|
||||
if (typesMatch) {
|
||||
return a >= b;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
throw new Error(`Invalid compare operator: '${op}'. Use eq, ne, lt, gt, lte, or gte.`);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,76 +6,78 @@
|
|||
|
||||
import { isValidUrl } from '../../../common/lib/url';
|
||||
|
||||
export const containerStyle = () => ({
|
||||
name: 'containerStyle',
|
||||
aliases: [],
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
type: 'containerStyle',
|
||||
help:
|
||||
'Creates an object used for describing the properties of a series on a chart.' +
|
||||
' You would usually use this inside of a charting function',
|
||||
args: {
|
||||
border: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Valid CSS border string',
|
||||
export function containerStyle() {
|
||||
return {
|
||||
name: 'containerStyle',
|
||||
aliases: [],
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
borderRadius: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Number of pixels to use when rounding the border',
|
||||
type: 'containerStyle',
|
||||
help:
|
||||
'Creates an object used for describing the properties of a series on a chart.' +
|
||||
' You would usually use this inside of a charting function',
|
||||
args: {
|
||||
border: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Valid CSS border string',
|
||||
},
|
||||
borderRadius: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Number of pixels to use when rounding the border',
|
||||
},
|
||||
padding: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Content distance in pixels from border',
|
||||
},
|
||||
backgroundColor: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Valid CSS background color string',
|
||||
},
|
||||
backgroundImage: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Valid CSS background image string',
|
||||
},
|
||||
backgroundSize: {
|
||||
types: ['string'],
|
||||
help: 'Valid CSS background size string',
|
||||
default: 'contain',
|
||||
options: ['contain', 'cover', 'auto'],
|
||||
},
|
||||
backgroundRepeat: {
|
||||
types: ['string'],
|
||||
help: 'Valid CSS background repeat string',
|
||||
default: 'no-repeat',
|
||||
options: ['repeat-x', 'repeat', 'space', 'round', 'no-repeat', 'space'],
|
||||
},
|
||||
opacity: {
|
||||
types: ['number', 'null'],
|
||||
help: 'A number between 0 and 1 representing the degree of transparency of the element',
|
||||
},
|
||||
overflow: {
|
||||
types: ['string'],
|
||||
help: 'Sets overflow of the container',
|
||||
options: ['visible', 'hidden', 'scroll', 'auto'],
|
||||
},
|
||||
},
|
||||
padding: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Content distance in pixels from border',
|
||||
},
|
||||
backgroundColor: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Valid CSS background color string',
|
||||
},
|
||||
backgroundImage: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Valid CSS background image string',
|
||||
},
|
||||
backgroundSize: {
|
||||
types: ['string'],
|
||||
help: 'Valid CSS background size string',
|
||||
default: 'contain',
|
||||
options: ['contain', 'cover', 'auto'],
|
||||
},
|
||||
backgroundRepeat: {
|
||||
types: ['string'],
|
||||
help: 'Valid CSS background repeat string',
|
||||
default: 'no-repeat',
|
||||
options: ['repeat-x', 'repeat', 'space', 'round', 'no-repeat', 'space'],
|
||||
},
|
||||
opacity: {
|
||||
types: ['number', 'null'],
|
||||
help: 'A number between 0 and 1 representing the degree of transparency of the element',
|
||||
},
|
||||
overflow: {
|
||||
types: ['string'],
|
||||
help: 'Sets overflow of the container',
|
||||
options: ['visible', 'hidden', 'scroll', 'auto'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const { backgroundImage, backgroundSize, backgroundRepeat, ...remainingArgs } = args;
|
||||
const style = {
|
||||
type: 'containerStyle',
|
||||
...remainingArgs,
|
||||
};
|
||||
fn: (context, args) => {
|
||||
const { backgroundImage, backgroundSize, backgroundRepeat, ...remainingArgs } = args;
|
||||
const style = {
|
||||
type: 'containerStyle',
|
||||
...remainingArgs,
|
||||
};
|
||||
|
||||
if (backgroundImage) {
|
||||
if (!isValidUrl(backgroundImage)) {
|
||||
throw new Error('Invalid backgroundImage. Please provide an asset or a URL.');
|
||||
if (backgroundImage) {
|
||||
if (!isValidUrl(backgroundImage)) {
|
||||
throw new Error('Invalid backgroundImage. Please provide an asset or a URL.');
|
||||
}
|
||||
style.backgroundImage = `url(${backgroundImage})`;
|
||||
style.backgroundSize = backgroundSize;
|
||||
style.backgroundRepeat = backgroundRepeat;
|
||||
}
|
||||
style.backgroundImage = `url(${backgroundImage})`;
|
||||
style.backgroundSize = backgroundSize;
|
||||
style.backgroundRepeat = backgroundRepeat;
|
||||
}
|
||||
|
||||
// removes keys with undefined value
|
||||
return JSON.parse(JSON.stringify(style));
|
||||
},
|
||||
});
|
||||
// removes keys with undefined value
|
||||
return JSON.parse(JSON.stringify(style));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const context = () => ({
|
||||
name: 'context',
|
||||
help:
|
||||
'Returns whatever you pass into it. This can be useful when you need to use context as argument to a function as a sub-expression',
|
||||
args: {},
|
||||
fn: context => {
|
||||
return context;
|
||||
},
|
||||
});
|
||||
export function context() {
|
||||
return {
|
||||
name: 'context',
|
||||
help:
|
||||
'Returns whatever you pass into it. This can be useful when you need to use context as argument to a function as a sub-expression',
|
||||
args: {},
|
||||
fn: context => {
|
||||
return context;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,75 +6,77 @@
|
|||
|
||||
import Papa from 'papaparse';
|
||||
|
||||
export const csv = () => ({
|
||||
name: 'csv',
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
args: {
|
||||
data: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'CSV data to use',
|
||||
export function csv() {
|
||||
return {
|
||||
name: 'csv',
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
delimiter: {
|
||||
types: ['string'],
|
||||
help: 'Data separation character',
|
||||
},
|
||||
newline: {
|
||||
types: ['string'],
|
||||
help: 'Row separation character',
|
||||
},
|
||||
},
|
||||
help: 'Create datatable from csv input',
|
||||
fn(context, args) {
|
||||
const { data: csvString, delimiter, newline } = args;
|
||||
|
||||
const config = {
|
||||
transform: val => {
|
||||
if (val.indexOf('"') >= 0) {
|
||||
const trimmed = val.trim();
|
||||
return trimmed.replace(/(^\"|\"$)/g, '');
|
||||
}
|
||||
return val;
|
||||
args: {
|
||||
data: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'CSV data to use',
|
||||
},
|
||||
};
|
||||
|
||||
if (delimiter != null) {
|
||||
config.delimiter = delimiter;
|
||||
}
|
||||
if (newline != null) {
|
||||
config.newline = newline;
|
||||
}
|
||||
|
||||
// TODO: handle errors, check output.errors
|
||||
const output = Papa.parse(csvString, config);
|
||||
|
||||
// output.data is an array of arrays, rows and values in each row
|
||||
return output.data.reduce(
|
||||
(acc, row, i) => {
|
||||
if (i === 0) {
|
||||
// first row, assume header values
|
||||
row.forEach(colName => acc.columns.push({ name: colName.trim(), type: 'string' }));
|
||||
} else {
|
||||
// any other row is a data row
|
||||
const rowObj = row.reduce((rowAcc, colValue, j) => {
|
||||
const colName = acc.columns[j].name;
|
||||
rowAcc[colName] = colValue;
|
||||
return rowAcc;
|
||||
}, {});
|
||||
|
||||
acc.rows.push(rowObj);
|
||||
}
|
||||
|
||||
return acc;
|
||||
delimiter: {
|
||||
types: ['string'],
|
||||
help: 'Data separation character',
|
||||
},
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [],
|
||||
rows: [],
|
||||
newline: {
|
||||
types: ['string'],
|
||||
help: 'Row separation character',
|
||||
},
|
||||
},
|
||||
help: 'Create datatable from csv input',
|
||||
fn(context, args) {
|
||||
const { data: csvString, delimiter, newline } = args;
|
||||
|
||||
const config = {
|
||||
transform: val => {
|
||||
if (val.indexOf('"') >= 0) {
|
||||
const trimmed = val.trim();
|
||||
return trimmed.replace(/(^\"|\"$)/g, '');
|
||||
}
|
||||
return val;
|
||||
},
|
||||
};
|
||||
|
||||
if (delimiter != null) {
|
||||
config.delimiter = delimiter;
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
if (newline != null) {
|
||||
config.newline = newline;
|
||||
}
|
||||
|
||||
// TODO: handle errors, check output.errors
|
||||
const output = Papa.parse(csvString, config);
|
||||
|
||||
// output.data is an array of arrays, rows and values in each row
|
||||
return output.data.reduce(
|
||||
(acc, row, i) => {
|
||||
if (i === 0) {
|
||||
// first row, assume header values
|
||||
row.forEach(colName => acc.columns.push({ name: colName.trim(), type: 'string' }));
|
||||
} else {
|
||||
// any other row is a data row
|
||||
const rowObj = row.reduce((rowAcc, colValue, j) => {
|
||||
const colName = acc.columns[j].name;
|
||||
rowAcc[colName] = colValue;
|
||||
return rowAcc;
|
||||
}, {});
|
||||
|
||||
acc.rows.push(rowObj);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [],
|
||||
rows: [],
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,36 +16,40 @@ const getInputDate = input => {
|
|||
return input;
|
||||
};
|
||||
|
||||
export const date = () => ({
|
||||
name: 'date',
|
||||
type: 'number',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
help: 'Returns the current time, or a time parsed from a string, as milliseconds since epoch',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['string', 'null'],
|
||||
help:
|
||||
'An optional date string to parse into milliseconds since epoch ' +
|
||||
'Can be either a valid Javascript Date input or a string to parse using the format argument. Must be an ISO 8601 string or you must provide the format',
|
||||
export function date() {
|
||||
return {
|
||||
name: 'date',
|
||||
type: 'number',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
format: {
|
||||
types: ['string'],
|
||||
help:
|
||||
'The momentJS format for parsing the optional date string (See https://momentjs.com/docs/#/displaying/)',
|
||||
help: 'Returns the current time, or a time parsed from a string, as milliseconds since epoch',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['string', 'null'],
|
||||
help:
|
||||
'An optional date string to parse into milliseconds since epoch ' +
|
||||
'Can be either a valid Javascript Date input or a string to parse using the format argument. Must be an ISO 8601 string or you must provide the format',
|
||||
},
|
||||
format: {
|
||||
types: ['string'],
|
||||
help:
|
||||
'The momentJS format for parsing the optional date string (See https://momentjs.com/docs/#/displaying/)',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const { value: date, format } = args;
|
||||
const useMoment = date && format;
|
||||
const outputDate = useMoment ? moment.utc(date, format).toDate() : new Date(getInputDate(date));
|
||||
fn: (context, args) => {
|
||||
const { value: date, format } = args;
|
||||
const useMoment = date && format;
|
||||
const outputDate = useMoment
|
||||
? moment.utc(date, format).toDate()
|
||||
: new Date(getInputDate(date));
|
||||
|
||||
if (isNaN(outputDate.getTime())) {
|
||||
throw new Error(`Invalid date input: ${date}`);
|
||||
}
|
||||
if (isNaN(outputDate.getTime())) {
|
||||
throw new Error(`Invalid date input: ${date}`);
|
||||
}
|
||||
|
||||
return outputDate.valueOf();
|
||||
},
|
||||
});
|
||||
return outputDate.valueOf();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,17 +4,19 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const doFn = () => ({
|
||||
name: 'do',
|
||||
help:
|
||||
'Runs multiple sub-expressions. Returns the passed in context. Nice for running actions producing functions.',
|
||||
args: {
|
||||
fn: {
|
||||
aliases: ['_'],
|
||||
multi: true,
|
||||
help:
|
||||
'One or more sub-expressions. The value of these is not available in the root pipeline as this function simply returns the passed in context',
|
||||
export function doFn() {
|
||||
return {
|
||||
name: 'do',
|
||||
help:
|
||||
'Runs multiple sub-expressions. Returns the passed in context. Nice for running actions producing functions.',
|
||||
args: {
|
||||
fn: {
|
||||
aliases: ['_'],
|
||||
multi: true,
|
||||
help:
|
||||
'One or more sub-expressions. The value of these is not available in the root pipeline as this function simply returns the passed in context',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: context => context,
|
||||
});
|
||||
fn: context => context,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,39 +6,41 @@
|
|||
|
||||
import { uniq } from 'lodash';
|
||||
|
||||
export const dropdownControl = () => ({
|
||||
name: 'dropdownControl',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
help: 'Configure a drop down filter control element',
|
||||
args: {
|
||||
filterColumn: {
|
||||
type: ['string'],
|
||||
help: 'The column or field to attach the filter to',
|
||||
export function dropdownControl() {
|
||||
return {
|
||||
name: 'dropdownControl',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
valueColumn: {
|
||||
type: ['string'],
|
||||
help: 'The datatable column from which to extract the unique values for the drop down',
|
||||
},
|
||||
},
|
||||
fn: (context, { valueColumn, filterColumn }) => {
|
||||
let choices = [];
|
||||
if (context.rows[0][valueColumn]) {
|
||||
choices = uniq(context.rows.map(row => row[valueColumn])).sort();
|
||||
}
|
||||
|
||||
const column = filterColumn || valueColumn;
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'dropdown_filter',
|
||||
value: {
|
||||
column,
|
||||
choices,
|
||||
help: 'Configure a drop down filter control element',
|
||||
args: {
|
||||
filterColumn: {
|
||||
type: ['string'],
|
||||
help: 'The column or field to attach the filter to',
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
valueColumn: {
|
||||
type: ['string'],
|
||||
help: 'The datatable column from which to extract the unique values for the drop down',
|
||||
},
|
||||
},
|
||||
fn: (context, { valueColumn, filterColumn }) => {
|
||||
let choices = [];
|
||||
if (context.rows[0][valueColumn]) {
|
||||
choices = uniq(context.rows.map(row => row[valueColumn])).sort();
|
||||
}
|
||||
|
||||
const column = filterColumn || valueColumn;
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'dropdown_filter',
|
||||
value: {
|
||||
column,
|
||||
choices,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,19 +4,21 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const eq = () => ({
|
||||
name: 'eq',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is equal to the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
export function eq() {
|
||||
return {
|
||||
name: 'eq',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is equal to the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
return context === args.value;
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
return context === args.value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,35 +4,37 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const exactly = () => ({
|
||||
name: 'exactly',
|
||||
aliases: [],
|
||||
type: 'filter',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
help: 'Create a filter that matches a given column for a perfectly exact value',
|
||||
args: {
|
||||
column: {
|
||||
types: ['string'],
|
||||
aliases: ['field', 'c'],
|
||||
help: 'The column or field to attach the filter to',
|
||||
export function exactly() {
|
||||
return {
|
||||
name: 'exactly',
|
||||
aliases: [],
|
||||
type: 'filter',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
value: {
|
||||
types: ['string'],
|
||||
aliases: ['v', 'val'],
|
||||
help: 'The value to match exactly, including white space and capitalization',
|
||||
help: 'Create a filter that matches a given column for a perfectly exact value',
|
||||
args: {
|
||||
column: {
|
||||
types: ['string'],
|
||||
aliases: ['field', 'c'],
|
||||
help: 'The column or field to attach the filter to',
|
||||
},
|
||||
value: {
|
||||
types: ['string'],
|
||||
aliases: ['v', 'val'],
|
||||
help: 'The value to match exactly, including white space and capitalization',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const { value, column } = args;
|
||||
fn: (context, args) => {
|
||||
const { value, column } = args;
|
||||
|
||||
const filter = {
|
||||
type: 'exactly',
|
||||
value,
|
||||
column,
|
||||
};
|
||||
const filter = {
|
||||
type: 'exactly',
|
||||
value,
|
||||
column,
|
||||
};
|
||||
|
||||
return { ...context, and: [...context.and, filter] };
|
||||
},
|
||||
});
|
||||
return { ...context, and: [...context.and, filter] };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,37 +4,39 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const filterrows = () => ({
|
||||
name: 'filterrows',
|
||||
aliases: [],
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
help: 'Filter rows in a datatable based on the return value of a subexpression.',
|
||||
args: {
|
||||
fn: {
|
||||
resolve: false,
|
||||
aliases: ['_'],
|
||||
types: ['boolean'],
|
||||
help:
|
||||
'An expression to pass each rows in the datatable into. The expression should return a boolean. ' +
|
||||
'A true value will preserve the row, and a false value will remove it.',
|
||||
export function filterrows() {
|
||||
return {
|
||||
name: 'filterrows',
|
||||
aliases: [],
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
},
|
||||
fn(context, { fn }) {
|
||||
const checks = context.rows.map(row =>
|
||||
fn({
|
||||
...context,
|
||||
rows: [row],
|
||||
})
|
||||
);
|
||||
help: 'Filter rows in a datatable based on the return value of a subexpression.',
|
||||
args: {
|
||||
fn: {
|
||||
resolve: false,
|
||||
aliases: ['_'],
|
||||
types: ['boolean'],
|
||||
help:
|
||||
'An expression to pass each rows in the datatable into. The expression should return a boolean. ' +
|
||||
'A true value will preserve the row, and a false value will remove it.',
|
||||
},
|
||||
},
|
||||
fn(context, { fn }) {
|
||||
const checks = context.rows.map(row =>
|
||||
fn({
|
||||
...context,
|
||||
rows: [row],
|
||||
})
|
||||
);
|
||||
|
||||
return Promise.all(checks)
|
||||
.then(results => context.rows.filter((row, i) => results[i]))
|
||||
.then(rows => ({
|
||||
...context,
|
||||
rows,
|
||||
}));
|
||||
},
|
||||
});
|
||||
return Promise.all(checks)
|
||||
.then(results => context.rows.filter((row, i) => results[i]))
|
||||
.then(rows => ({
|
||||
...context,
|
||||
rows,
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -24,90 +24,92 @@ const weights = [
|
|||
];
|
||||
const alignments = ['center', 'left', 'right', 'justify'];
|
||||
|
||||
export const font = () => ({
|
||||
name: 'font',
|
||||
aliases: [],
|
||||
type: 'style',
|
||||
help: 'Create a font style',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
args: {
|
||||
size: {
|
||||
types: ['number'],
|
||||
help: 'Font size (px)',
|
||||
default: 14,
|
||||
export function font() {
|
||||
return {
|
||||
name: 'font',
|
||||
aliases: [],
|
||||
type: 'style',
|
||||
help: 'Create a font style',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
lHeight: {
|
||||
types: ['number'],
|
||||
aliases: ['lineHeight'],
|
||||
help: 'Line height (px)',
|
||||
args: {
|
||||
size: {
|
||||
types: ['number'],
|
||||
help: 'Font size (px)',
|
||||
default: 14,
|
||||
},
|
||||
lHeight: {
|
||||
types: ['number'],
|
||||
aliases: ['lineHeight'],
|
||||
help: 'Line height (px)',
|
||||
},
|
||||
family: {
|
||||
types: ['string'],
|
||||
default: `"${openSans.value}"`,
|
||||
help: 'An acceptable CSS web font string',
|
||||
},
|
||||
color: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Text color',
|
||||
},
|
||||
weight: {
|
||||
types: ['string'],
|
||||
help:
|
||||
'Set the font weight, e.g. normal, bold, bolder, lighter, 100, 200, 300, 400, 500, 600, 700, 800, 900',
|
||||
default: 'normal',
|
||||
options: weights,
|
||||
},
|
||||
underline: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: 'Underline the text, true or false',
|
||||
options: [true, false],
|
||||
},
|
||||
italic: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: 'Italicize, true or false',
|
||||
options: [true, false],
|
||||
},
|
||||
align: {
|
||||
types: ['string'],
|
||||
help: 'Horizontal text alignment',
|
||||
default: 'left',
|
||||
options: alignments,
|
||||
},
|
||||
},
|
||||
family: {
|
||||
types: ['string'],
|
||||
default: `"${openSans.value}"`,
|
||||
help: 'An acceptable CSS web font string',
|
||||
},
|
||||
color: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Text color',
|
||||
},
|
||||
weight: {
|
||||
types: ['string'],
|
||||
help:
|
||||
'Set the font weight, e.g. normal, bold, bolder, lighter, 100, 200, 300, 400, 500, 600, 700, 800, 900',
|
||||
default: 'normal',
|
||||
options: weights,
|
||||
},
|
||||
underline: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: 'Underline the text, true or false',
|
||||
options: [true, false],
|
||||
},
|
||||
italic: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: 'Italicize, true or false',
|
||||
options: [true, false],
|
||||
},
|
||||
align: {
|
||||
types: ['string'],
|
||||
help: 'Horizontal text alignment',
|
||||
default: 'left',
|
||||
options: alignments,
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!weights.includes(args.weight)) {
|
||||
throw new Error(`Invalid font weight: '${args.weight}'`);
|
||||
}
|
||||
if (!alignments.includes(args.align)) {
|
||||
throw new Error(`Invalid text alignment: '${args.align}'`);
|
||||
}
|
||||
fn: (context, args) => {
|
||||
if (!weights.includes(args.weight)) {
|
||||
throw new Error(`Invalid font weight: '${args.weight}'`);
|
||||
}
|
||||
if (!alignments.includes(args.align)) {
|
||||
throw new Error(`Invalid text alignment: '${args.align}'`);
|
||||
}
|
||||
|
||||
// the line height shouldn't ever be lower than the size
|
||||
const lineHeight = args.lHeight ? `${args.lHeight}px` : 1;
|
||||
// the line height shouldn't ever be lower than the size
|
||||
const lineHeight = args.lHeight ? `${args.lHeight}px` : 1;
|
||||
|
||||
const spec = {
|
||||
fontFamily: args.family,
|
||||
fontWeight: args.weight,
|
||||
fontStyle: args.italic ? 'italic' : 'normal',
|
||||
textDecoration: args.underline ? 'underline' : 'none',
|
||||
textAlign: args.align,
|
||||
fontSize: `${args.size}px`, // apply font size as a pixel setting
|
||||
lineHeight: lineHeight, // apply line height as a pixel setting
|
||||
};
|
||||
const spec = {
|
||||
fontFamily: args.family,
|
||||
fontWeight: args.weight,
|
||||
fontStyle: args.italic ? 'italic' : 'normal',
|
||||
textDecoration: args.underline ? 'underline' : 'none',
|
||||
textAlign: args.align,
|
||||
fontSize: `${args.size}px`, // apply font size as a pixel setting
|
||||
lineHeight: lineHeight, // apply line height as a pixel setting
|
||||
};
|
||||
|
||||
// conditionally apply styles based on input
|
||||
if (args.color) {
|
||||
spec.color = args.color;
|
||||
}
|
||||
// conditionally apply styles based on input
|
||||
if (args.color) {
|
||||
spec.color = args.color;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'style',
|
||||
spec,
|
||||
css: inlineStyle(spec),
|
||||
};
|
||||
},
|
||||
});
|
||||
return {
|
||||
type: 'style',
|
||||
spec,
|
||||
css: inlineStyle(spec),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,24 +5,26 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
export const formatdate = () => ({
|
||||
name: 'formatdate',
|
||||
type: 'string',
|
||||
help: 'Output a ms since epoch number as a formatted string',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
args: {
|
||||
format: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'MomentJS Format with which to bucket (See https://momentjs.com/docs/#/displaying/)',
|
||||
export function formatdate() {
|
||||
return {
|
||||
name: 'formatdate',
|
||||
type: 'string',
|
||||
help: 'Output a ms since epoch number as a formatted string',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.format) {
|
||||
return moment.utc(new Date(context)).toISOString();
|
||||
}
|
||||
return moment.utc(new Date(context)).format(args.format);
|
||||
},
|
||||
});
|
||||
args: {
|
||||
format: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'MomentJS Format with which to bucket (See https://momentjs.com/docs/#/displaying/)',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.format) {
|
||||
return moment.utc(new Date(context)).toISOString();
|
||||
}
|
||||
return moment.utc(new Date(context)).format(args.format);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,24 +6,26 @@
|
|||
|
||||
import numeral from '@elastic/numeral';
|
||||
|
||||
export const formatnumber = () => ({
|
||||
name: 'formatnumber',
|
||||
type: 'string',
|
||||
help: 'Turn a number into a string using a NumberJS format',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
args: {
|
||||
format: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'NumeralJS format string http://numeraljs.com/#format',
|
||||
export function formatnumber() {
|
||||
return {
|
||||
name: 'formatnumber',
|
||||
type: 'string',
|
||||
help: 'Turn a number into a string using a NumberJS format',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.format) {
|
||||
return String(context);
|
||||
}
|
||||
return numeral(context).format(args.format);
|
||||
},
|
||||
});
|
||||
args: {
|
||||
format: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'NumeralJS format string http://numeraljs.com/#format',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.format) {
|
||||
return String(context);
|
||||
}
|
||||
return numeral(context).format(args.format);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,38 +4,40 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const getCell = () => ({
|
||||
name: 'getCell',
|
||||
help: 'Fetch a single cell in a table',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
column: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'c'],
|
||||
help: 'The name of the column value to fetch',
|
||||
export function getCell() {
|
||||
return {
|
||||
name: 'getCell',
|
||||
help: 'Fetch a single cell in a table',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
row: {
|
||||
types: ['number'],
|
||||
aliases: ['r'],
|
||||
help: 'The row number, starting at 0',
|
||||
default: 0,
|
||||
args: {
|
||||
column: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'c'],
|
||||
help: 'The name of the column value to fetch',
|
||||
},
|
||||
row: {
|
||||
types: ['number'],
|
||||
aliases: ['r'],
|
||||
help: 'The row number, starting at 0',
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const row = context.rows[args.row];
|
||||
if (!row) {
|
||||
throw new Error(`Row not found: '${args.row}'`);
|
||||
}
|
||||
fn: (context, args) => {
|
||||
const row = context.rows[args.row];
|
||||
if (!row) {
|
||||
throw new Error(`Row not found: '${args.row}'`);
|
||||
}
|
||||
|
||||
const { column = context.columns[0].name } = args;
|
||||
const value = row[column];
|
||||
const { column = context.columns[0].name } = args;
|
||||
const value = row[column];
|
||||
|
||||
if (typeof value === 'undefined') {
|
||||
throw new Error(`Column not found: '${column}'`);
|
||||
}
|
||||
if (typeof value === 'undefined') {
|
||||
throw new Error(`Column not found: '${column}'`);
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
});
|
||||
return value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,22 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const gt = () => ({
|
||||
name: 'gt',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is greater than the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
export function gt() {
|
||||
return {
|
||||
name: 'gt',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is greater than the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (typeof context !== typeof args.value) {
|
||||
return false;
|
||||
}
|
||||
return context > args.value;
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
if (typeof context !== typeof args.value) {
|
||||
return false;
|
||||
}
|
||||
return context > args.value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,22 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const gte = () => ({
|
||||
name: 'gte',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is greater than or equal to the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
export function gte() {
|
||||
return {
|
||||
name: 'gte',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is greater than or equal to the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (typeof context !== typeof args.value) {
|
||||
return false;
|
||||
}
|
||||
return context >= args.value;
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
if (typeof context !== typeof args.value) {
|
||||
return false;
|
||||
}
|
||||
return context >= args.value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,24 +6,26 @@
|
|||
|
||||
import { take } from 'lodash';
|
||||
|
||||
export const head = () => ({
|
||||
name: 'head',
|
||||
aliases: [],
|
||||
type: 'datatable',
|
||||
help: 'Get the first N rows from the datatable. Also see `tail`',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
count: {
|
||||
aliases: ['_'],
|
||||
types: ['number'],
|
||||
help: 'Return this many rows from the beginning of the datatable',
|
||||
default: 1,
|
||||
export function head() {
|
||||
return {
|
||||
name: 'head',
|
||||
aliases: [],
|
||||
type: 'datatable',
|
||||
help: 'Get the first N rows from the datatable. Also see `tail`',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => ({
|
||||
...context,
|
||||
rows: take(context.rows, args.count),
|
||||
}),
|
||||
});
|
||||
args: {
|
||||
count: {
|
||||
aliases: ['_'],
|
||||
types: ['number'],
|
||||
help: 'Return this many rows from the beginning of the datatable',
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
fn: (context, args) => ({
|
||||
...context,
|
||||
rows: take(context.rows, args.count),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,38 +4,40 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const ifFn = () => ({
|
||||
name: 'if',
|
||||
help: 'Perform conditional logic',
|
||||
args: {
|
||||
condition: {
|
||||
types: ['boolean', 'null'],
|
||||
aliases: ['_'],
|
||||
help:
|
||||
'A boolean true or false, usually returned by a subexpression. If this is not supplied then the input context will be used',
|
||||
export function ifFn() {
|
||||
return {
|
||||
name: 'if',
|
||||
help: 'Perform conditional logic',
|
||||
args: {
|
||||
condition: {
|
||||
types: ['boolean', 'null'],
|
||||
aliases: ['_'],
|
||||
help:
|
||||
'A boolean true or false, usually returned by a subexpression. If this is not supplied then the input context will be used',
|
||||
},
|
||||
then: {
|
||||
resolve: false,
|
||||
help: 'The return value if true',
|
||||
},
|
||||
else: {
|
||||
resolve: false,
|
||||
help:
|
||||
'The return value if false. If else is not specified, and the condition is false' +
|
||||
'then the input context to the function will be returned',
|
||||
},
|
||||
},
|
||||
then: {
|
||||
resolve: false,
|
||||
help: 'The return value if true',
|
||||
},
|
||||
else: {
|
||||
resolve: false,
|
||||
help:
|
||||
'The return value if false. If else is not specified, and the condition is false' +
|
||||
'then the input context to the function will be returned',
|
||||
},
|
||||
},
|
||||
fn: async (context, args) => {
|
||||
if (args.condition) {
|
||||
if (typeof args.then === 'undefined') {
|
||||
return context;
|
||||
fn: async (context, args) => {
|
||||
if (args.condition) {
|
||||
if (typeof args.then === 'undefined') {
|
||||
return context;
|
||||
}
|
||||
return await args.then();
|
||||
} else {
|
||||
if (typeof args.else === 'undefined') {
|
||||
return context;
|
||||
}
|
||||
return await args.else();
|
||||
}
|
||||
return await args.then();
|
||||
} else {
|
||||
if (typeof args.else === 'undefined') {
|
||||
return context;
|
||||
}
|
||||
return await args.else();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,43 +9,45 @@ import { elasticLogo } from '../../lib/elastic_logo';
|
|||
|
||||
const modes = ['contain', 'cover', 'stretch'];
|
||||
|
||||
export const image = () => ({
|
||||
name: 'image',
|
||||
aliases: [],
|
||||
type: 'image',
|
||||
help: 'Display an image',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
args: {
|
||||
dataurl: {
|
||||
// This was accepting dataurl, but there was no facility in fn for checking type and handling a dataurl type.
|
||||
types: ['string', 'null'],
|
||||
help: 'The HTTP(S) URL or base64 data of an image.',
|
||||
aliases: ['_', 'url'],
|
||||
default: elasticLogo,
|
||||
export function image() {
|
||||
return {
|
||||
name: 'image',
|
||||
aliases: [],
|
||||
type: 'image',
|
||||
help: 'Display an image',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
mode: {
|
||||
types: ['string', 'null'],
|
||||
help:
|
||||
'"contain" will show the entire image, scaled to fit.' +
|
||||
'"cover" will fill the container with the image, cropping from the sides or bottom as needed.' +
|
||||
'"stretch" will resize the height and width of the image to 100% of the container',
|
||||
default: 'contain',
|
||||
options: modes,
|
||||
args: {
|
||||
dataurl: {
|
||||
// This was accepting dataurl, but there was no facility in fn for checking type and handling a dataurl type.
|
||||
types: ['string', 'null'],
|
||||
help: 'The HTTP(S) URL or base64 data of an image.',
|
||||
aliases: ['_', 'url'],
|
||||
default: elasticLogo,
|
||||
},
|
||||
mode: {
|
||||
types: ['string', 'null'],
|
||||
help:
|
||||
'"contain" will show the entire image, scaled to fit.' +
|
||||
'"cover" will fill the container with the image, cropping from the sides or bottom as needed.' +
|
||||
'"stretch" will resize the height and width of the image to 100% of the container',
|
||||
default: 'contain',
|
||||
options: modes,
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, { dataurl, mode }) => {
|
||||
if (!modes.includes(mode)) {
|
||||
throw '"mode" must be "contain", "cover", or "stretch"';
|
||||
}
|
||||
fn: (context, { dataurl, mode }) => {
|
||||
if (!modes.includes(mode)) {
|
||||
throw '"mode" must be "contain", "cover", or "stretch"';
|
||||
}
|
||||
|
||||
const modeStyle = mode === 'stretch' ? '100% 100%' : mode;
|
||||
const modeStyle = mode === 'stretch' ? '100% 100%' : mode;
|
||||
|
||||
return {
|
||||
type: 'image',
|
||||
mode: modeStyle,
|
||||
dataurl: resolveWithMissingImage(dataurl, elasticLogo),
|
||||
};
|
||||
},
|
||||
});
|
||||
return {
|
||||
type: 'image',
|
||||
mode: modeStyle,
|
||||
dataurl: resolveWithMissingImage(dataurl, elasticLogo),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,22 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const lt = () => ({
|
||||
name: 'lt',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is less than the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
export function lt() {
|
||||
return {
|
||||
name: 'lt',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is less than the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (typeof context !== typeof args.value) {
|
||||
return false;
|
||||
}
|
||||
return context < args.value;
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
if (typeof context !== typeof args.value) {
|
||||
return false;
|
||||
}
|
||||
return context < args.value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,22 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const lte = () => ({
|
||||
name: 'lte',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is less than or equal to the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
export function lte() {
|
||||
return {
|
||||
name: 'lte',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is less than or equal to the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (typeof context !== typeof args.value) {
|
||||
return false;
|
||||
}
|
||||
return context <= args.value;
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
if (typeof context !== typeof args.value) {
|
||||
return false;
|
||||
}
|
||||
return context <= args.value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,60 +6,62 @@
|
|||
|
||||
import { getType } from '@kbn/interpreter/common';
|
||||
|
||||
export const mapColumn = () => ({
|
||||
name: 'mapColumn',
|
||||
aliases: ['mc'], // midnight commander. So many times I've launched midnight commander instead of moving a file.
|
||||
type: 'datatable',
|
||||
help: 'Add a column calculated as the result of other columns, or not',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
name: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'column'],
|
||||
help: 'The name of the resulting column',
|
||||
required: true,
|
||||
export function mapColumn() {
|
||||
return {
|
||||
name: 'mapColumn',
|
||||
aliases: ['mc'], // midnight commander. So many times I've launched midnight commander instead of moving a file.
|
||||
type: 'datatable',
|
||||
help: 'Add a column calculated as the result of other columns, or not',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
expression: {
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
resolve: false,
|
||||
aliases: ['exp', 'fn'],
|
||||
help: 'A canvas expression which will be passed each row as a single row datatable',
|
||||
args: {
|
||||
name: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'column'],
|
||||
help: 'The name of the resulting column',
|
||||
required: true,
|
||||
},
|
||||
expression: {
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
resolve: false,
|
||||
aliases: ['exp', 'fn'],
|
||||
help: 'A canvas expression which will be passed each row as a single row datatable',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
args.expression = args.expression || (() => Promise.resolve(null));
|
||||
fn: (context, args) => {
|
||||
args.expression = args.expression || (() => Promise.resolve(null));
|
||||
|
||||
const columns = [...context.columns];
|
||||
const rowPromises = context.rows.map(row => {
|
||||
return args
|
||||
.expression({
|
||||
const columns = [...context.columns];
|
||||
const rowPromises = context.rows.map(row => {
|
||||
return args
|
||||
.expression({
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows: [row],
|
||||
})
|
||||
.then(val => ({
|
||||
...row,
|
||||
[args.name]: val,
|
||||
}));
|
||||
});
|
||||
|
||||
return Promise.all(rowPromises).then(rows => {
|
||||
const existingColumnIndex = columns.findIndex(({ name }) => name === args.name);
|
||||
const type = rows.length ? getType(rows[0][args.name]) : 'null';
|
||||
const newColumn = { name: args.name, type };
|
||||
if (existingColumnIndex === -1) {
|
||||
columns.push(newColumn);
|
||||
} else {
|
||||
columns[existingColumnIndex] = newColumn;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows: [row],
|
||||
})
|
||||
.then(val => ({
|
||||
...row,
|
||||
[args.name]: val,
|
||||
}));
|
||||
});
|
||||
|
||||
return Promise.all(rowPromises).then(rows => {
|
||||
const existingColumnIndex = columns.findIndex(({ name }) => name === args.name);
|
||||
const type = rows.length ? getType(rows[0][args.name]) : 'null';
|
||||
const newColumn = { name: args.name, type };
|
||||
if (existingColumnIndex === -1) {
|
||||
columns.push(newColumn);
|
||||
} else {
|
||||
columns[existingColumnIndex] = newColumn;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
rows,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,52 +7,54 @@
|
|||
import { evaluate } from 'tinymath';
|
||||
import { pivotObjectArray } from '../../../common/lib/pivot_object_array';
|
||||
|
||||
export const math = () => ({
|
||||
name: 'math',
|
||||
type: 'number',
|
||||
help:
|
||||
'Interpret a math expression, with a number or datatable as context. Datatable columns are available by their column name. ' +
|
||||
'If you pass in a number it is available as "value" (without the quotes)',
|
||||
context: {
|
||||
types: ['number', 'datatable'],
|
||||
},
|
||||
args: {
|
||||
expression: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help:
|
||||
'An evaluated TinyMath expression. (See [TinyMath Functions](https://www.elastic.co/guide/en/kibana/current/canvas-tinymath-functions.html))',
|
||||
export function math() {
|
||||
return {
|
||||
name: 'math',
|
||||
type: 'number',
|
||||
help:
|
||||
'Interpret a math expression, with a number or datatable as context. Datatable columns are available by their column name. ' +
|
||||
'If you pass in a number it is available as "value" (without the quotes)',
|
||||
context: {
|
||||
types: ['number', 'datatable'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.expression || args.expression.trim() === '') {
|
||||
throw new Error('Empty expression');
|
||||
}
|
||||
args: {
|
||||
expression: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help:
|
||||
'An evaluated TinyMath expression. (See [TinyMath Functions](https://www.elastic.co/guide/en/kibana/current/canvas-tinymath-functions.html))',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.expression || args.expression.trim() === '') {
|
||||
throw new Error('Empty expression');
|
||||
}
|
||||
|
||||
const isDatatable = context && context.type === 'datatable';
|
||||
const mathContext = isDatatable
|
||||
? pivotObjectArray(context.rows, context.columns.map(col => col.name))
|
||||
: { value: context };
|
||||
try {
|
||||
const result = evaluate(args.expression, mathContext);
|
||||
if (Array.isArray(result)) {
|
||||
if (result.length === 1) {
|
||||
return result[0];
|
||||
const isDatatable = context && context.type === 'datatable';
|
||||
const mathContext = isDatatable
|
||||
? pivotObjectArray(context.rows, context.columns.map(col => col.name))
|
||||
: { value: context };
|
||||
try {
|
||||
const result = evaluate(args.expression, mathContext);
|
||||
if (Array.isArray(result)) {
|
||||
if (result.length === 1) {
|
||||
return result[0];
|
||||
}
|
||||
throw new Error(
|
||||
'Expressions must return a single number. Try wrapping your expression in mean() or sum()'
|
||||
);
|
||||
}
|
||||
if (isNaN(result)) {
|
||||
throw new Error('Failed to execute math expression. Check your column names');
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (context.rows.length === 0) {
|
||||
throw new Error('Empty datatable');
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
throw new Error(
|
||||
'Expressions must return a single number. Try wrapping your expression in mean() or sum()'
|
||||
);
|
||||
}
|
||||
if (isNaN(result)) {
|
||||
throw new Error('Failed to execute math expression. Check your column names');
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (context.rows.length === 0) {
|
||||
throw new Error('Empty datatable');
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,42 +5,46 @@
|
|||
*/
|
||||
|
||||
import { openSans } from '../../../common/lib/fonts';
|
||||
export const metric = () => ({
|
||||
name: 'metric',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'A number with a label',
|
||||
context: {
|
||||
types: ['string', 'null'],
|
||||
},
|
||||
args: {
|
||||
label: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'text', 'description'],
|
||||
help: 'Text describing the metric',
|
||||
default: '""',
|
||||
export function metric() {
|
||||
return {
|
||||
name: 'metric',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'A number with a label',
|
||||
context: {
|
||||
types: ['string', 'null'],
|
||||
},
|
||||
metricFont: {
|
||||
types: ['style'],
|
||||
help: 'Font settings for the metric. Technically you can stick other styles in here too!',
|
||||
default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`,
|
||||
},
|
||||
labelFont: {
|
||||
types: ['style'],
|
||||
help: 'Font settings for the label. Technically you can stick other styles in here too!',
|
||||
default: `{font size=14 family="${openSans.value}" color="#000000" align=center}`,
|
||||
},
|
||||
},
|
||||
fn: (context, { label, metricFont, labelFont }) => {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'metric',
|
||||
value: {
|
||||
metric: context === null ? '?' : context,
|
||||
label,
|
||||
metricFont,
|
||||
labelFont,
|
||||
args: {
|
||||
label: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'text', 'description'],
|
||||
help: 'Text describing the metric',
|
||||
default: '""',
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
metricFont: {
|
||||
types: ['style'],
|
||||
help: 'Font settings for the metric. Technically you can stick other styles in here too!',
|
||||
default: `{font size=48 family="${
|
||||
openSans.value
|
||||
}" color="#000000" align=center lHeight=48}`,
|
||||
},
|
||||
labelFont: {
|
||||
types: ['style'],
|
||||
help: 'Font settings for the label. Technically you can stick other styles in here too!',
|
||||
default: `{font size=14 family="${openSans.value}" color="#000000" align=center}`,
|
||||
},
|
||||
},
|
||||
fn: (context, { label, metricFont, labelFont }) => {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'metric',
|
||||
value: {
|
||||
metric: context === null ? '?' : context,
|
||||
label,
|
||||
metricFont,
|
||||
labelFont,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,19 +4,21 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const neq = () => ({
|
||||
name: 'neq',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is not equal to the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
export function neq() {
|
||||
return {
|
||||
name: 'neq',
|
||||
type: 'boolean',
|
||||
help: 'Return if the context is not equal to the argument',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['boolean', 'number', 'string', 'null'],
|
||||
required: true,
|
||||
help: 'The value to compare the context to',
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
return context !== args.value;
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
return context !== args.value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,40 +5,42 @@
|
|||
*/
|
||||
|
||||
import { palettes } from '../../../common/lib/palettes';
|
||||
export const palette = () => ({
|
||||
name: 'palette',
|
||||
aliases: [],
|
||||
type: 'palette',
|
||||
help: 'Create a color palette',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
args: {
|
||||
color: {
|
||||
aliases: ['_'],
|
||||
multi: true,
|
||||
types: ['string'],
|
||||
help: 'Palette colors, rgba, hex, or HTML color string. Pass this multiple times.',
|
||||
export function palette() {
|
||||
return {
|
||||
name: 'palette',
|
||||
aliases: [],
|
||||
type: 'palette',
|
||||
help: 'Create a color palette',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
gradient: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: 'Prefer to make a gradient where supported and useful?',
|
||||
options: [true, false],
|
||||
args: {
|
||||
color: {
|
||||
aliases: ['_'],
|
||||
multi: true,
|
||||
types: ['string'],
|
||||
help: 'Palette colors, rgba, hex, or HTML color string. Pass this multiple times.',
|
||||
},
|
||||
gradient: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: 'Prefer to make a gradient where supported and useful?',
|
||||
options: [true, false],
|
||||
},
|
||||
reverse: {
|
||||
type: ['boolean'],
|
||||
default: false,
|
||||
help: 'Reverse the palette',
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
reverse: {
|
||||
type: ['boolean'],
|
||||
default: false,
|
||||
help: 'Reverse the palette',
|
||||
options: [true, false],
|
||||
fn: (context, args) => {
|
||||
const colors = [].concat(args.color || palettes.paul_tor_14.colors);
|
||||
return {
|
||||
type: 'palette',
|
||||
colors: args.reverse ? colors.reverse() : colors,
|
||||
gradient: args.gradient,
|
||||
};
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const colors = [].concat(args.color || palettes.paul_tor_14.colors);
|
||||
return {
|
||||
type: 'palette',
|
||||
colors: args.reverse ? colors.reverse() : colors,
|
||||
gradient: args.gradient,
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,116 +9,118 @@ import { get, map, groupBy } from 'lodash';
|
|||
import { getColorsFromPalette } from '../../../common/lib/get_colors_from_palette';
|
||||
import { getLegendConfig } from '../../../common/lib/get_legend_config';
|
||||
|
||||
export const pie = () => ({
|
||||
name: 'pie',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a pie chart element',
|
||||
context: {
|
||||
types: ['pointseries'],
|
||||
},
|
||||
args: {
|
||||
palette: {
|
||||
types: ['palette', 'null'],
|
||||
help: 'A palette object for describing the colors to use on this pie',
|
||||
default: '{palette}',
|
||||
export function pie() {
|
||||
return {
|
||||
name: 'pie',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a pie chart element',
|
||||
context: {
|
||||
types: ['pointseries'],
|
||||
},
|
||||
seriesStyle: {
|
||||
multi: true,
|
||||
types: ['seriesStyle', 'null'],
|
||||
help: 'A style of a specific series',
|
||||
args: {
|
||||
palette: {
|
||||
types: ['palette', 'null'],
|
||||
help: 'A palette object for describing the colors to use on this pie',
|
||||
default: '{palette}',
|
||||
},
|
||||
seriesStyle: {
|
||||
multi: true,
|
||||
types: ['seriesStyle', 'null'],
|
||||
help: 'A style of a specific series',
|
||||
},
|
||||
radius: {
|
||||
type: ['string', 'number'],
|
||||
help: `Radius of the pie as a percentage (between 0 and 1) of the available space. Set to 'auto' to automatically set radius`,
|
||||
default: 'auto',
|
||||
},
|
||||
hole: {
|
||||
types: ['number'],
|
||||
default: 0,
|
||||
help: 'Draw a hole in the pie, 0-100, as a percentage of the pie radius',
|
||||
},
|
||||
labels: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: 'Show pie labels',
|
||||
options: [true, false],
|
||||
},
|
||||
labelRadius: {
|
||||
types: ['number'],
|
||||
default: 100,
|
||||
help: 'Percentage of area of container to use as radius for the label circle',
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: 'Label font',
|
||||
default: '{font}',
|
||||
},
|
||||
legend: {
|
||||
types: ['string', 'boolean'],
|
||||
help: 'Legend position, nw, sw, ne, se or false',
|
||||
default: false,
|
||||
options: ['nw', 'sw', 'ne', 'se', false],
|
||||
},
|
||||
tilt: {
|
||||
types: ['number'],
|
||||
default: 1,
|
||||
help: 'Percentage of tilt where 1 is fully vertical and 0 is completely flat',
|
||||
},
|
||||
},
|
||||
radius: {
|
||||
type: ['string', 'number'],
|
||||
help: `Radius of the pie as a percentage (between 0 and 1) of the available space. Set to 'auto' to automatically set radius`,
|
||||
default: 'auto',
|
||||
},
|
||||
hole: {
|
||||
types: ['number'],
|
||||
default: 0,
|
||||
help: 'Draw a hole in the pie, 0-100, as a percentage of the pie radius',
|
||||
},
|
||||
labels: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: 'Show pie labels',
|
||||
options: [true, false],
|
||||
},
|
||||
labelRadius: {
|
||||
types: ['number'],
|
||||
default: 100,
|
||||
help: 'Percentage of area of container to use as radius for the label circle',
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: 'Label font',
|
||||
default: '{font}',
|
||||
},
|
||||
legend: {
|
||||
types: ['string', 'boolean'],
|
||||
help: 'Legend position, nw, sw, ne, se or false',
|
||||
default: false,
|
||||
options: ['nw', 'sw', 'ne', 'se', false],
|
||||
},
|
||||
tilt: {
|
||||
types: ['number'],
|
||||
default: 1,
|
||||
help: 'Percentage of tilt where 1 is fully vertical and 0 is completely flat',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const seriesStyles = keyBy(args.seriesStyle || [], 'label') || {};
|
||||
fn: (context, args) => {
|
||||
const seriesStyles = keyBy(args.seriesStyle || [], 'label') || {};
|
||||
|
||||
const data = map(groupBy(context.rows, 'color'), (series, label) => {
|
||||
const item = {
|
||||
label: label,
|
||||
data: series.map(point => point.size || 1),
|
||||
};
|
||||
const data = map(groupBy(context.rows, 'color'), (series, label) => {
|
||||
const item = {
|
||||
label: label,
|
||||
data: series.map(point => point.size || 1),
|
||||
};
|
||||
|
||||
const seriesStyle = seriesStyles[label];
|
||||
const seriesStyle = seriesStyles[label];
|
||||
|
||||
// append series style, if there is a match
|
||||
if (seriesStyle) {
|
||||
item.color = get(seriesStyle, 'color');
|
||||
}
|
||||
// append series style, if there is a match
|
||||
if (seriesStyle) {
|
||||
item.color = get(seriesStyle, 'color');
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
return item;
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'pie',
|
||||
value: {
|
||||
font: args.font,
|
||||
data,
|
||||
options: {
|
||||
canvas: false,
|
||||
colors: getColorsFromPalette(args.palette, data.length),
|
||||
legend: getLegendConfig(args.legend, data.length),
|
||||
grid: {
|
||||
show: false,
|
||||
},
|
||||
series: {
|
||||
pie: {
|
||||
show: true,
|
||||
innerRadius: Math.max(args.hole, 0) / 100,
|
||||
stroke: {
|
||||
width: 0,
|
||||
},
|
||||
label: {
|
||||
show: args.labels,
|
||||
radius: (args.labelRadius >= 0 ? args.labelRadius : 100) / 100,
|
||||
},
|
||||
tilt: args.tilt,
|
||||
radius: args.radius,
|
||||
},
|
||||
bubbles: {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'pie',
|
||||
value: {
|
||||
font: args.font,
|
||||
data,
|
||||
options: {
|
||||
canvas: false,
|
||||
colors: getColorsFromPalette(args.palette, data.length),
|
||||
legend: getLegendConfig(args.legend, data.length),
|
||||
grid: {
|
||||
show: false,
|
||||
},
|
||||
shadowSize: 0,
|
||||
series: {
|
||||
pie: {
|
||||
show: true,
|
||||
innerRadius: Math.max(args.hole, 0) / 100,
|
||||
stroke: {
|
||||
width: 0,
|
||||
},
|
||||
label: {
|
||||
show: args.labels,
|
||||
radius: (args.labelRadius >= 0 ? args.labelRadius : 100) / 100,
|
||||
},
|
||||
tilt: args.tilt,
|
||||
radius: args.radius,
|
||||
},
|
||||
bubbles: {
|
||||
show: false,
|
||||
},
|
||||
shadowSize: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,136 +13,138 @@ import { getFontSpec } from './get_font_spec';
|
|||
import { seriesStyleToFlot } from './series_style_to_flot';
|
||||
import { getTickHash } from './get_tick_hash';
|
||||
|
||||
export const plot = () => ({
|
||||
name: 'plot',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a plot element',
|
||||
context: {
|
||||
types: ['pointseries'],
|
||||
},
|
||||
args: {
|
||||
seriesStyle: {
|
||||
multi: true,
|
||||
types: ['seriesStyle', 'null'],
|
||||
help: 'A style of a specific series',
|
||||
export function plot() {
|
||||
return {
|
||||
name: 'plot',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a plot element',
|
||||
context: {
|
||||
types: ['pointseries'],
|
||||
},
|
||||
defaultStyle: {
|
||||
multi: false,
|
||||
types: ['seriesStyle'],
|
||||
help: 'The default style to use for every series',
|
||||
default: '{seriesStyle points=5}',
|
||||
},
|
||||
palette: {
|
||||
types: ['palette'],
|
||||
help: 'A palette object for describing the colors to use on this plot',
|
||||
default: '{palette}',
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: 'Legend and tick mark fonts',
|
||||
default: '{font}',
|
||||
},
|
||||
legend: {
|
||||
types: ['string', 'boolean'],
|
||||
help: 'Legend position, nw, sw, ne, se or false',
|
||||
default: 'ne',
|
||||
options: ['nw', 'sw', 'ne', 'se', false],
|
||||
},
|
||||
yaxis: {
|
||||
types: ['boolean', 'axisConfig'],
|
||||
help: 'Axis configuration, or false to disable',
|
||||
default: true,
|
||||
},
|
||||
xaxis: {
|
||||
types: ['boolean', 'axisConfig'],
|
||||
help: 'Axis configuration, or false to disable',
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const seriesStyles = keyBy(args.seriesStyle || [], 'label') || {};
|
||||
const sortedRows = sortBy(context.rows, ['x', 'y', 'color', 'size', 'text']);
|
||||
const ticks = getTickHash(context.columns, sortedRows);
|
||||
const font = args.font ? getFontSpec(args.font) : {};
|
||||
|
||||
const data = map(groupBy(sortedRows, 'color'), (series, label) => {
|
||||
const seriesStyle = {
|
||||
...args.defaultStyle,
|
||||
...seriesStyles[label],
|
||||
};
|
||||
const flotStyle = seriesStyle ? seriesStyleToFlot(seriesStyle) : {};
|
||||
|
||||
return {
|
||||
...flotStyle,
|
||||
label: label,
|
||||
data: series.map(point => {
|
||||
const attrs = {};
|
||||
const x = get(context.columns, 'x.type') === 'string' ? ticks.x.hash[point.x] : point.x;
|
||||
const y = get(context.columns, 'y.type') === 'string' ? ticks.y.hash[point.y] : point.y;
|
||||
|
||||
if (point.size != null) {
|
||||
attrs.size = point.size;
|
||||
} else if (get(seriesStyle, 'points')) {
|
||||
attrs.size = seriesStyle.points;
|
||||
set(flotStyle, 'bubbles.size.min', seriesStyle.points);
|
||||
}
|
||||
|
||||
if (point.text != null) {
|
||||
attrs.text = point.text;
|
||||
}
|
||||
|
||||
return [x, y, attrs];
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const gridConfig = {
|
||||
borderWidth: 0,
|
||||
borderColor: null,
|
||||
color: 'rgba(0,0,0,0)',
|
||||
labelMargin: 30,
|
||||
margin: {
|
||||
right: 30,
|
||||
top: 20,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
args: {
|
||||
seriesStyle: {
|
||||
multi: true,
|
||||
types: ['seriesStyle', 'null'],
|
||||
help: 'A style of a specific series',
|
||||
},
|
||||
};
|
||||
defaultStyle: {
|
||||
multi: false,
|
||||
types: ['seriesStyle'],
|
||||
help: 'The default style to use for every series',
|
||||
default: '{seriesStyle points=5}',
|
||||
},
|
||||
palette: {
|
||||
types: ['palette'],
|
||||
help: 'A palette object for describing the colors to use on this plot',
|
||||
default: '{palette}',
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: 'Legend and tick mark fonts',
|
||||
default: '{font}',
|
||||
},
|
||||
legend: {
|
||||
types: ['string', 'boolean'],
|
||||
help: 'Legend position, nw, sw, ne, se or false',
|
||||
default: 'ne',
|
||||
options: ['nw', 'sw', 'ne', 'se', false],
|
||||
},
|
||||
yaxis: {
|
||||
types: ['boolean', 'axisConfig'],
|
||||
help: 'Axis configuration, or false to disable',
|
||||
default: true,
|
||||
},
|
||||
xaxis: {
|
||||
types: ['boolean', 'axisConfig'],
|
||||
help: 'Axis configuration, or false to disable',
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const seriesStyles = keyBy(args.seriesStyle || [], 'label') || {};
|
||||
const sortedRows = sortBy(context.rows, ['x', 'y', 'color', 'size', 'text']);
|
||||
const ticks = getTickHash(context.columns, sortedRows);
|
||||
const font = args.font ? getFontSpec(args.font) : {};
|
||||
|
||||
const result = {
|
||||
type: 'render',
|
||||
as: 'plot',
|
||||
value: {
|
||||
font: args.font,
|
||||
data: sortBy(data, 'label'),
|
||||
options: {
|
||||
canvas: false,
|
||||
colors: getColorsFromPalette(args.palette, data.length),
|
||||
legend: getLegendConfig(args.legend, data.length),
|
||||
grid: gridConfig,
|
||||
xaxis: getFlotAxisConfig('x', args.xaxis, {
|
||||
columns: context.columns,
|
||||
ticks,
|
||||
font,
|
||||
const data = map(groupBy(sortedRows, 'color'), (series, label) => {
|
||||
const seriesStyle = {
|
||||
...args.defaultStyle,
|
||||
...seriesStyles[label],
|
||||
};
|
||||
const flotStyle = seriesStyle ? seriesStyleToFlot(seriesStyle) : {};
|
||||
|
||||
return {
|
||||
...flotStyle,
|
||||
label: label,
|
||||
data: series.map(point => {
|
||||
const attrs = {};
|
||||
const x = get(context.columns, 'x.type') === 'string' ? ticks.x.hash[point.x] : point.x;
|
||||
const y = get(context.columns, 'y.type') === 'string' ? ticks.y.hash[point.y] : point.y;
|
||||
|
||||
if (point.size != null) {
|
||||
attrs.size = point.size;
|
||||
} else if (get(seriesStyle, 'points')) {
|
||||
attrs.size = seriesStyle.points;
|
||||
set(flotStyle, 'bubbles.size.min', seriesStyle.points);
|
||||
}
|
||||
|
||||
if (point.text != null) {
|
||||
attrs.text = point.text;
|
||||
}
|
||||
|
||||
return [x, y, attrs];
|
||||
}),
|
||||
yaxis: getFlotAxisConfig('y', args.yaxis, {
|
||||
columns: context.columns,
|
||||
ticks,
|
||||
font,
|
||||
}),
|
||||
series: {
|
||||
shadowSize: 0,
|
||||
...seriesStyleToFlot(args.defaultStyle),
|
||||
};
|
||||
});
|
||||
|
||||
const gridConfig = {
|
||||
borderWidth: 0,
|
||||
borderColor: null,
|
||||
color: 'rgba(0,0,0,0)',
|
||||
labelMargin: 30,
|
||||
margin: {
|
||||
right: 30,
|
||||
top: 20,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
},
|
||||
};
|
||||
|
||||
const result = {
|
||||
type: 'render',
|
||||
as: 'plot',
|
||||
value: {
|
||||
font: args.font,
|
||||
data: sortBy(data, 'label'),
|
||||
options: {
|
||||
canvas: false,
|
||||
colors: getColorsFromPalette(args.palette, data.length),
|
||||
legend: getLegendConfig(args.legend, data.length),
|
||||
grid: gridConfig,
|
||||
xaxis: getFlotAxisConfig('x', args.xaxis, {
|
||||
columns: context.columns,
|
||||
ticks,
|
||||
font,
|
||||
}),
|
||||
yaxis: getFlotAxisConfig('y', args.yaxis, {
|
||||
columns: context.columns,
|
||||
ticks,
|
||||
font,
|
||||
}),
|
||||
series: {
|
||||
shadowSize: 0,
|
||||
...seriesStyleToFlot(args.defaultStyle),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// fix the issue of plot sometimes re-rendering with an empty chart
|
||||
// TODO: holy hell, why does this work?! the working theory is that some values become undefined
|
||||
// and serializing the result here causes them to be dropped off, and this makes flot react differently.
|
||||
// It's also possible that something else ends up mutating this object, but that seems less likely.
|
||||
return JSON.parse(JSON.stringify(result));
|
||||
},
|
||||
});
|
||||
// fix the issue of plot sometimes re-rendering with an empty chart
|
||||
// TODO: holy hell, why does this work?! the working theory is that some values become undefined
|
||||
// and serializing the result here causes them to be dropped off, and this makes flot react differently.
|
||||
// It's also possible that something else ends up mutating this object, but that seems less likely.
|
||||
return JSON.parse(JSON.stringify(result));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -55,89 +55,93 @@ function combineAcross(datatableArray) {
|
|||
};
|
||||
}
|
||||
|
||||
export const ply = () => ({
|
||||
name: 'ply',
|
||||
type: 'datatable',
|
||||
help:
|
||||
'Subdivide a datatable and pass the resulting tables into an expression, then merge the output',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
by: {
|
||||
types: ['string'],
|
||||
help: 'The column to subdivide on',
|
||||
multi: true,
|
||||
},
|
||||
expression: {
|
||||
export function ply() {
|
||||
return {
|
||||
name: 'ply',
|
||||
type: 'datatable',
|
||||
help:
|
||||
'Subdivide a datatable and pass the resulting tables into an expression, then merge the output',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
resolve: false,
|
||||
multi: true,
|
||||
aliases: ['fn', 'function'],
|
||||
help:
|
||||
'An expression to pass each resulting data table into. Tips: \n' +
|
||||
' Expressions must return a datatable. Use `as` to turn literals into datatables.\n' +
|
||||
' Multiple expressions must return the same number of rows.' +
|
||||
' If you need to return a differing row count, pipe into another instance of ply.\n' +
|
||||
' If multiple expressions return the same columns, the last one wins.',
|
||||
},
|
||||
// In the future it may make sense to add things like shape, or tooltip values, but I think what we have is good for now
|
||||
// The way the function below is written you can add as many arbitrary named args as you want.
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args) {
|
||||
return context;
|
||||
}
|
||||
let byColumns;
|
||||
let originalDatatables;
|
||||
args: {
|
||||
by: {
|
||||
types: ['string'],
|
||||
help: 'The column to subdivide on',
|
||||
multi: true,
|
||||
},
|
||||
expression: {
|
||||
types: ['datatable'],
|
||||
resolve: false,
|
||||
multi: true,
|
||||
aliases: ['fn', 'function'],
|
||||
help:
|
||||
'An expression to pass each resulting data table into. Tips: \n' +
|
||||
' Expressions must return a datatable. Use `as` to turn literals into datatables.\n' +
|
||||
' Multiple expressions must return the same number of rows.' +
|
||||
' If you need to return a differing row count, pipe into another instance of ply.\n' +
|
||||
' If multiple expressions return the same columns, the last one wins.',
|
||||
},
|
||||
// In the future it may make sense to add things like shape, or tooltip values, but I think what we have is good for now
|
||||
// The way the function below is written you can add as many arbitrary named args as you want.
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args) {
|
||||
return context;
|
||||
}
|
||||
let byColumns;
|
||||
let originalDatatables;
|
||||
|
||||
if (args.by) {
|
||||
byColumns = args.by.map(by => {
|
||||
const column = context.columns.find(column => column.name === by);
|
||||
if (!column) {
|
||||
throw new Error(`Column not found: '${by}'`);
|
||||
}
|
||||
return column;
|
||||
});
|
||||
const keyedDatatables = groupBy(context.rows, row => JSON.stringify(pick(row, args.by)));
|
||||
originalDatatables = Object.values(keyedDatatables).map(rows => ({
|
||||
...context,
|
||||
rows,
|
||||
}));
|
||||
} else {
|
||||
originalDatatables = [context];
|
||||
}
|
||||
|
||||
const datatablePromises = originalDatatables.map(originalDatatable => {
|
||||
let expressionResultPromises = [];
|
||||
|
||||
if (args.expression) {
|
||||
expressionResultPromises = args.expression.map(expression => expression(originalDatatable));
|
||||
if (args.by) {
|
||||
byColumns = args.by.map(by => {
|
||||
const column = context.columns.find(column => column.name === by);
|
||||
if (!column) {
|
||||
throw new Error(`Column not found: '${by}'`);
|
||||
}
|
||||
return column;
|
||||
});
|
||||
const keyedDatatables = groupBy(context.rows, row => JSON.stringify(pick(row, args.by)));
|
||||
originalDatatables = Object.values(keyedDatatables).map(rows => ({
|
||||
...context,
|
||||
rows,
|
||||
}));
|
||||
} else {
|
||||
expressionResultPromises.push(Promise.resolve(originalDatatable));
|
||||
originalDatatables = [context];
|
||||
}
|
||||
|
||||
return Promise.all(expressionResultPromises).then(combineAcross);
|
||||
});
|
||||
const datatablePromises = originalDatatables.map(originalDatatable => {
|
||||
let expressionResultPromises = [];
|
||||
|
||||
return Promise.all(datatablePromises).then(newDatatables => {
|
||||
// Here we're just merging each for the by splits, so it doesn't actually matter if the rows are the same length
|
||||
const columns = combineColumns([byColumns].concat(map(newDatatables, 'columns')));
|
||||
const rows = flatten(
|
||||
newDatatables.map((dt, i) => {
|
||||
const byColumnValues = pick(originalDatatables[i].rows[0], args.by);
|
||||
return dt.rows.map(row => ({
|
||||
...byColumnValues,
|
||||
...row,
|
||||
}));
|
||||
})
|
||||
);
|
||||
if (args.expression) {
|
||||
expressionResultPromises = args.expression.map(expression =>
|
||||
expression(originalDatatable)
|
||||
);
|
||||
} else {
|
||||
expressionResultPromises.push(Promise.resolve(originalDatatable));
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'datatable',
|
||||
rows,
|
||||
columns,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
return Promise.all(expressionResultPromises).then(combineAcross);
|
||||
});
|
||||
|
||||
return Promise.all(datatablePromises).then(newDatatables => {
|
||||
// Here we're just merging each for the by splits, so it doesn't actually matter if the rows are the same length
|
||||
const columns = combineColumns([byColumns].concat(map(newDatatables, 'columns')));
|
||||
const rows = flatten(
|
||||
newDatatables.map((dt, i) => {
|
||||
const byColumnValues = pick(originalDatatables[i].rows[0], args.by);
|
||||
return dt.rows.map(row => ({
|
||||
...byColumnValues,
|
||||
...row,
|
||||
}));
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'datatable',
|
||||
rows,
|
||||
columns,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,87 +18,89 @@ const shapes = [
|
|||
'wheel',
|
||||
];
|
||||
|
||||
export const progress = () => ({
|
||||
name: 'progress',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a progress element',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
args: {
|
||||
shape: {
|
||||
type: ['string'],
|
||||
alias: ['_'],
|
||||
help: `Select ${shapes.slice(0, -1).join(', ')}, or ${shapes.slice(-1)[0]}`,
|
||||
options: shapes,
|
||||
default: 'gauge',
|
||||
export function progress() {
|
||||
return {
|
||||
name: 'progress',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a progress element',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
max: {
|
||||
type: ['number'],
|
||||
help: 'Maximum value of the progress element',
|
||||
default: 1,
|
||||
},
|
||||
valueColor: {
|
||||
type: ['string'],
|
||||
help: 'Color of the progress bar',
|
||||
default: `#1785b0`,
|
||||
},
|
||||
barColor: {
|
||||
type: ['string'],
|
||||
help: 'Color of the background bar',
|
||||
default: `#f0f0f0`,
|
||||
},
|
||||
valueWeight: {
|
||||
type: ['number'],
|
||||
help: 'Thickness of the progress bar',
|
||||
default: 20,
|
||||
},
|
||||
barWeight: {
|
||||
type: ['number'],
|
||||
help: 'Thickness of the background bar',
|
||||
default: 20,
|
||||
},
|
||||
label: {
|
||||
type: ['boolean', 'string'],
|
||||
help: `Set true/false to show/hide label or provide a string to display as the label`,
|
||||
default: true,
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: 'Font settings for the label. Technically you can stick other styles in here too!',
|
||||
default: `{font size=24 family="${openSans.value}" color="#000000" align=center}`,
|
||||
},
|
||||
},
|
||||
fn: (value, args) => {
|
||||
if (args.max <= 0) {
|
||||
throw new Error(`Invalid max value: '${args.max}'. 'max' must be greater than 0`);
|
||||
}
|
||||
if (value > args.max || value < 0) {
|
||||
throw new Error(`Invalid value: '${value}'. Value must be between 0 and ${args.max}`);
|
||||
}
|
||||
|
||||
let label = '';
|
||||
if (args.label) {
|
||||
label = typeof args.label === 'string' ? args.label : `${value}`;
|
||||
}
|
||||
|
||||
let font = {};
|
||||
|
||||
if (get(args, 'font.spec')) {
|
||||
font = { ...args.font };
|
||||
font.spec.fill = args.font.spec.color; // SVG <text> uses fill for font color
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'progress',
|
||||
value: {
|
||||
value,
|
||||
...args,
|
||||
label,
|
||||
font,
|
||||
args: {
|
||||
shape: {
|
||||
type: ['string'],
|
||||
alias: ['_'],
|
||||
help: `Select ${shapes.slice(0, -1).join(', ')}, or ${shapes.slice(-1)[0]}`,
|
||||
options: shapes,
|
||||
default: 'gauge',
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
max: {
|
||||
type: ['number'],
|
||||
help: 'Maximum value of the progress element',
|
||||
default: 1,
|
||||
},
|
||||
valueColor: {
|
||||
type: ['string'],
|
||||
help: 'Color of the progress bar',
|
||||
default: `#1785b0`,
|
||||
},
|
||||
barColor: {
|
||||
type: ['string'],
|
||||
help: 'Color of the background bar',
|
||||
default: `#f0f0f0`,
|
||||
},
|
||||
valueWeight: {
|
||||
type: ['number'],
|
||||
help: 'Thickness of the progress bar',
|
||||
default: 20,
|
||||
},
|
||||
barWeight: {
|
||||
type: ['number'],
|
||||
help: 'Thickness of the background bar',
|
||||
default: 20,
|
||||
},
|
||||
label: {
|
||||
type: ['boolean', 'string'],
|
||||
help: `Set true/false to show/hide label or provide a string to display as the label`,
|
||||
default: true,
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: 'Font settings for the label. Technically you can stick other styles in here too!',
|
||||
default: `{font size=24 family="${openSans.value}" color="#000000" align=center}`,
|
||||
},
|
||||
},
|
||||
fn: (value, args) => {
|
||||
if (args.max <= 0) {
|
||||
throw new Error(`Invalid max value: '${args.max}'. 'max' must be greater than 0`);
|
||||
}
|
||||
if (value > args.max || value < 0) {
|
||||
throw new Error(`Invalid value: '${value}'. Value must be between 0 and ${args.max}`);
|
||||
}
|
||||
|
||||
let label = '';
|
||||
if (args.label) {
|
||||
label = typeof args.label === 'string' ? args.label : `${value}`;
|
||||
}
|
||||
|
||||
let font = {};
|
||||
|
||||
if (get(args, 'font.spec')) {
|
||||
font = { ...args.font };
|
||||
font.spec.fill = args.font.spec.color; // SVG <text> uses fill for font color
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'progress',
|
||||
value: {
|
||||
value,
|
||||
...args,
|
||||
label,
|
||||
font,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,37 +4,39 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const render = () => ({
|
||||
name: 'render',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Render an input as a specific element and set element level options such as styling',
|
||||
context: {
|
||||
types: ['render'],
|
||||
},
|
||||
args: {
|
||||
as: {
|
||||
types: ['string', 'null'],
|
||||
help:
|
||||
'The element type to use in rendering. You probably want a specialized function instead, such as plot or grid',
|
||||
options: ['debug', 'error', 'image', 'pie', 'plot', 'shape', 'table', 'text'],
|
||||
export function render() {
|
||||
return {
|
||||
name: 'render',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Render an input as a specific element and set element level options such as styling',
|
||||
context: {
|
||||
types: ['render'],
|
||||
},
|
||||
css: {
|
||||
types: ['string', 'null'],
|
||||
default: '"* > * {}"',
|
||||
help: 'Any block of custom CSS to be scoped to this element.',
|
||||
args: {
|
||||
as: {
|
||||
types: ['string', 'null'],
|
||||
help:
|
||||
'The element type to use in rendering. You probably want a specialized function instead, such as plot or grid',
|
||||
options: ['debug', 'error', 'image', 'pie', 'plot', 'shape', 'table', 'text'],
|
||||
},
|
||||
css: {
|
||||
types: ['string', 'null'],
|
||||
default: '"* > * {}"',
|
||||
help: 'Any block of custom CSS to be scoped to this element.',
|
||||
},
|
||||
containerStyle: {
|
||||
types: ['containerStyle', 'null'],
|
||||
help: 'Style for the container, including background, border, and opacity',
|
||||
},
|
||||
},
|
||||
containerStyle: {
|
||||
types: ['containerStyle', 'null'],
|
||||
help: 'Style for the container, including background, border, and opacity',
|
||||
fn: (context, args) => {
|
||||
return {
|
||||
...context,
|
||||
as: args.as || context.as,
|
||||
css: args.css,
|
||||
containerStyle: args.containerStyle,
|
||||
};
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
return {
|
||||
...context,
|
||||
as: args.as || context.as,
|
||||
css: args.css,
|
||||
containerStyle: args.containerStyle,
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,47 +7,49 @@
|
|||
import { resolveWithMissingImage } from '../../../common/lib/resolve_dataurl';
|
||||
import { elasticOutline } from '../../lib/elastic_outline';
|
||||
|
||||
export const repeatImage = () => ({
|
||||
name: 'repeatImage',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a repeating image element',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
args: {
|
||||
image: {
|
||||
types: ['string', 'null'],
|
||||
help: 'The image to repeat. Usually a dataURL or an asset',
|
||||
default: elasticOutline,
|
||||
},
|
||||
size: {
|
||||
export function repeatImage() {
|
||||
return {
|
||||
name: 'repeatImage',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a repeating image element',
|
||||
context: {
|
||||
types: ['number'],
|
||||
default: 100,
|
||||
help:
|
||||
'The maximum height or width of the image, in pixels. Eg, if you images is taller than it is wide, this will limit its height',
|
||||
},
|
||||
max: {
|
||||
types: ['number', 'null'],
|
||||
help: 'Maximum number of times the image may repeat',
|
||||
default: 1000,
|
||||
},
|
||||
emptyImage: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Fill the difference between the input and the `max=` parameter with this image',
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
fn: (count, args) => {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'repeatImage',
|
||||
value: {
|
||||
count: Math.floor(count),
|
||||
...args,
|
||||
image: resolveWithMissingImage(args.image, elasticOutline),
|
||||
emptyImage: resolveWithMissingImage(args.emptyImage),
|
||||
args: {
|
||||
image: {
|
||||
types: ['string', 'null'],
|
||||
help: 'The image to repeat. Usually a dataURL or an asset',
|
||||
default: elasticOutline,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
size: {
|
||||
types: ['number'],
|
||||
default: 100,
|
||||
help:
|
||||
'The maximum height or width of the image, in pixels. Eg, if you images is taller than it is wide, this will limit its height',
|
||||
},
|
||||
max: {
|
||||
types: ['number', 'null'],
|
||||
help: 'Maximum number of times the image may repeat',
|
||||
default: 1000,
|
||||
},
|
||||
emptyImage: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Fill the difference between the input and the `max=` parameter with this image',
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
fn: (count, args) => {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'repeatImage',
|
||||
value: {
|
||||
count: Math.floor(count),
|
||||
...args,
|
||||
image: resolveWithMissingImage(args.image, elasticOutline),
|
||||
emptyImage: resolveWithMissingImage(args.emptyImage),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,33 +4,35 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const replace = () => ({
|
||||
name: 'replace',
|
||||
type: 'string',
|
||||
help: 'Use a regular expression to replace parts of a string',
|
||||
context: {
|
||||
types: ['string'],
|
||||
},
|
||||
args: {
|
||||
pattern: {
|
||||
aliases: ['_', 'regex'],
|
||||
export function replace() {
|
||||
return {
|
||||
name: 'replace',
|
||||
type: 'string',
|
||||
help: 'Use a regular expression to replace parts of a string',
|
||||
context: {
|
||||
types: ['string'],
|
||||
help:
|
||||
'The text or pattern of a JavaScript regular expression, eg "[aeiou]". You can use capture groups here.',
|
||||
},
|
||||
flags: {
|
||||
aliases: ['modifiers'],
|
||||
types: ['string'],
|
||||
help:
|
||||
'Specify flags. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp for reference.',
|
||||
default: 'g',
|
||||
args: {
|
||||
pattern: {
|
||||
aliases: ['_', 'regex'],
|
||||
types: ['string'],
|
||||
help:
|
||||
'The text or pattern of a JavaScript regular expression, eg "[aeiou]". You can use capture groups here.',
|
||||
},
|
||||
flags: {
|
||||
aliases: ['modifiers'],
|
||||
types: ['string'],
|
||||
help:
|
||||
'Specify flags. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp for reference.',
|
||||
default: 'g',
|
||||
},
|
||||
replacement: {
|
||||
types: ['string'],
|
||||
help:
|
||||
'The replacement for the matching parts of string. Capture groups can be accessed by their index, eg $1',
|
||||
default: '""',
|
||||
},
|
||||
},
|
||||
replacement: {
|
||||
types: ['string'],
|
||||
help:
|
||||
'The replacement for the matching parts of string. Capture groups can be accessed by their index, eg $1',
|
||||
default: '""',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => context.replace(new RegExp(args.pattern, args.flags), args.replacement),
|
||||
});
|
||||
fn: (context, args) => context.replace(new RegExp(args.pattern, args.flags), args.replacement),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,46 +7,48 @@
|
|||
import { resolveWithMissingImage } from '../../../common/lib/resolve_dataurl';
|
||||
import { elasticOutline } from '../../lib/elastic_outline';
|
||||
|
||||
export const revealImage = () => ({
|
||||
name: 'revealImage',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a image reveal element',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
args: {
|
||||
image: {
|
||||
types: ['string', 'null'],
|
||||
help: 'The image to reveal',
|
||||
default: elasticOutline,
|
||||
export function revealImage() {
|
||||
return {
|
||||
name: 'revealImage',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a image reveal element',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
emptyImage: {
|
||||
types: ['string', 'null'],
|
||||
help: 'An optional background image to reveal over',
|
||||
default: null,
|
||||
},
|
||||
origin: {
|
||||
types: ['string'],
|
||||
help: 'Where to start from. Eg, top, left, bottom or right',
|
||||
default: 'bottom',
|
||||
options: ['top', 'left', 'bottom', 'right'],
|
||||
},
|
||||
},
|
||||
fn: (percent, args) => {
|
||||
if (percent > 1 || percent < 0) {
|
||||
throw new Error(`Invalid value: '${percent}'. Percentage must be between 0 and 1`);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'revealImage',
|
||||
value: {
|
||||
percent,
|
||||
...args,
|
||||
image: resolveWithMissingImage(args.image, elasticOutline),
|
||||
emptyImage: resolveWithMissingImage(args.emptyImage),
|
||||
args: {
|
||||
image: {
|
||||
types: ['string', 'null'],
|
||||
help: 'The image to reveal',
|
||||
default: elasticOutline,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
emptyImage: {
|
||||
types: ['string', 'null'],
|
||||
help: 'An optional background image to reveal over',
|
||||
default: null,
|
||||
},
|
||||
origin: {
|
||||
types: ['string'],
|
||||
help: 'Where to start from. Eg, top, left, bottom or right',
|
||||
default: 'bottom',
|
||||
options: ['top', 'left', 'bottom', 'right'],
|
||||
},
|
||||
},
|
||||
fn: (percent, args) => {
|
||||
if (percent > 1 || percent < 0) {
|
||||
throw new Error(`Invalid value: '${percent}'. Percentage must be between 0 and 1`);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'revealImage',
|
||||
value: {
|
||||
percent,
|
||||
...args,
|
||||
image: resolveWithMissingImage(args.image, elasticOutline),
|
||||
emptyImage: resolveWithMissingImage(args.emptyImage),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,25 +6,27 @@
|
|||
|
||||
import moment from 'moment';
|
||||
|
||||
export const rounddate = () => ({
|
||||
name: 'rounddate',
|
||||
type: 'number',
|
||||
help: 'Round ms since epoch using a moment formatting string. Returns ms since epoch',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
args: {
|
||||
format: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help:
|
||||
'MomentJS Format with which to bucket (See https://momentjs.com/docs/#/displaying/). For example "YYYY-MM" would round to the month',
|
||||
export function rounddate() {
|
||||
return {
|
||||
name: 'rounddate',
|
||||
type: 'number',
|
||||
help: 'Round ms since epoch using a moment formatting string. Returns ms since epoch',
|
||||
context: {
|
||||
types: ['number'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.format) {
|
||||
return context;
|
||||
}
|
||||
return moment.utc(moment.utc(context).format(args.format), args.format).valueOf();
|
||||
},
|
||||
});
|
||||
args: {
|
||||
format: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help:
|
||||
'MomentJS Format with which to bucket (See https://momentjs.com/docs/#/displaying/). For example "YYYY-MM" would round to the month',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.format) {
|
||||
return context;
|
||||
}
|
||||
return moment.utc(moment.utc(context).format(args.format), args.format).valueOf();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,15 +4,17 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const rowCount = () => ({
|
||||
name: 'rowCount',
|
||||
aliases: [],
|
||||
type: 'number',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
help:
|
||||
'Return the number of rows. Pair with ply to get the count of unique column values, or combinations of unique column values.',
|
||||
args: {},
|
||||
fn: context => context.rows.length,
|
||||
});
|
||||
export function rowCount() {
|
||||
return {
|
||||
name: 'rowCount',
|
||||
aliases: [],
|
||||
type: 'number',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
help:
|
||||
'Return the number of rows. Pair with ply to get the count of unique column values, or combinations of unique column values.',
|
||||
args: {},
|
||||
fn: context => context.rows.length,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,60 +6,62 @@
|
|||
|
||||
const name = 'seriesStyle';
|
||||
|
||||
export const seriesStyle = () => ({
|
||||
name,
|
||||
help:
|
||||
'Creates an object used for describing the properties of a series on a chart.' +
|
||||
' You would usually use this inside of a charting function',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
args: {
|
||||
label: {
|
||||
types: ['string'],
|
||||
displayName: 'Series label',
|
||||
help:
|
||||
'The label of the line this style applies to, not the name you would like to give the line',
|
||||
export function seriesStyle() {
|
||||
return {
|
||||
name,
|
||||
help:
|
||||
'Creates an object used for describing the properties of a series on a chart.' +
|
||||
' You would usually use this inside of a charting function',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
color: {
|
||||
types: ['string', 'null'],
|
||||
displayName: 'Color',
|
||||
help: 'Color to assign the line',
|
||||
args: {
|
||||
label: {
|
||||
types: ['string'],
|
||||
displayName: 'Series label',
|
||||
help:
|
||||
'The label of the line this style applies to, not the name you would like to give the line',
|
||||
},
|
||||
color: {
|
||||
types: ['string', 'null'],
|
||||
displayName: 'Color',
|
||||
help: 'Color to assign the line',
|
||||
},
|
||||
lines: {
|
||||
types: ['number'],
|
||||
displayName: 'Line width',
|
||||
help: 'Width of the line',
|
||||
},
|
||||
bars: {
|
||||
types: ['number'],
|
||||
displayName: 'Bar width',
|
||||
help: 'Width of bars',
|
||||
},
|
||||
points: {
|
||||
types: ['number'],
|
||||
displayName: 'Show points',
|
||||
help: 'Size of points on line',
|
||||
},
|
||||
fill: {
|
||||
types: ['number', 'boolean'],
|
||||
displayName: 'Fill points',
|
||||
help: 'Should we fill points?',
|
||||
default: false,
|
||||
options: [true, false],
|
||||
},
|
||||
stack: {
|
||||
types: ['number', 'null'],
|
||||
displayName: 'Stack series',
|
||||
help:
|
||||
'Should we stack the series? This is the stack "id". Series with the same stack id will be stacked together',
|
||||
},
|
||||
horizontalBars: {
|
||||
types: ['boolean'],
|
||||
displayName: 'Horizontal bars orientation',
|
||||
help: 'Sets the orientation of bars in the chart to horizontal',
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
lines: {
|
||||
types: ['number'],
|
||||
displayName: 'Line width',
|
||||
help: 'Width of the line',
|
||||
},
|
||||
bars: {
|
||||
types: ['number'],
|
||||
displayName: 'Bar width',
|
||||
help: 'Width of bars',
|
||||
},
|
||||
points: {
|
||||
types: ['number'],
|
||||
displayName: 'Show points',
|
||||
help: 'Size of points on line',
|
||||
},
|
||||
fill: {
|
||||
types: ['number', 'boolean'],
|
||||
displayName: 'Fill points',
|
||||
help: 'Should we fill points?',
|
||||
default: false,
|
||||
options: [true, false],
|
||||
},
|
||||
stack: {
|
||||
types: ['number', 'null'],
|
||||
displayName: 'Stack series',
|
||||
help:
|
||||
'Should we stack the series? This is the stack "id". Series with the same stack id will be stacked together',
|
||||
},
|
||||
horizontalBars: {
|
||||
types: ['boolean'],
|
||||
displayName: 'Horizontal bars orientation',
|
||||
help: 'Sets the orientation of bars in the chart to horizontal',
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => ({ type: name, ...args }),
|
||||
});
|
||||
fn: (context, args) => ({ type: name, ...args }),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,68 +4,70 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const shape = () => ({
|
||||
name: 'shape',
|
||||
aliases: [],
|
||||
type: 'shape',
|
||||
help: 'Create a shape',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
args: {
|
||||
shape: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Pick a shape',
|
||||
aliases: ['_'],
|
||||
default: 'square',
|
||||
options: [
|
||||
'arrow',
|
||||
'arrowMulti',
|
||||
'bookmark',
|
||||
'cross',
|
||||
'circle',
|
||||
'hexagon',
|
||||
'kite',
|
||||
'pentagon',
|
||||
'rhombus',
|
||||
'semicircle',
|
||||
'speechBubble',
|
||||
'square',
|
||||
'star',
|
||||
'tag',
|
||||
'triangle',
|
||||
'triangleRight',
|
||||
],
|
||||
},
|
||||
fill: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Valid CSS color string',
|
||||
default: 'black',
|
||||
},
|
||||
border: {
|
||||
types: ['string', 'null'],
|
||||
aliases: ['stroke'],
|
||||
help: 'Valid CSS color string',
|
||||
},
|
||||
borderWidth: {
|
||||
types: ['number', 'null'],
|
||||
aliases: ['strokeWidth'],
|
||||
help: 'Thickness of the border',
|
||||
default: '0',
|
||||
},
|
||||
maintainAspect: {
|
||||
types: ['boolean'],
|
||||
help: 'Select true to maintain aspect ratio',
|
||||
default: false,
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
fn: (context, { shape, fill, border, borderWidth, maintainAspect }) => ({
|
||||
export function shape() {
|
||||
return {
|
||||
name: 'shape',
|
||||
aliases: [],
|
||||
type: 'shape',
|
||||
shape,
|
||||
fill,
|
||||
border,
|
||||
borderWidth,
|
||||
maintainAspect,
|
||||
}),
|
||||
});
|
||||
help: 'Create a shape',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
args: {
|
||||
shape: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Pick a shape',
|
||||
aliases: ['_'],
|
||||
default: 'square',
|
||||
options: [
|
||||
'arrow',
|
||||
'arrowMulti',
|
||||
'bookmark',
|
||||
'cross',
|
||||
'circle',
|
||||
'hexagon',
|
||||
'kite',
|
||||
'pentagon',
|
||||
'rhombus',
|
||||
'semicircle',
|
||||
'speechBubble',
|
||||
'square',
|
||||
'star',
|
||||
'tag',
|
||||
'triangle',
|
||||
'triangleRight',
|
||||
],
|
||||
},
|
||||
fill: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Valid CSS color string',
|
||||
default: 'black',
|
||||
},
|
||||
border: {
|
||||
types: ['string', 'null'],
|
||||
aliases: ['stroke'],
|
||||
help: 'Valid CSS color string',
|
||||
},
|
||||
borderWidth: {
|
||||
types: ['number', 'null'],
|
||||
aliases: ['strokeWidth'],
|
||||
help: 'Thickness of the border',
|
||||
default: '0',
|
||||
},
|
||||
maintainAspect: {
|
||||
types: ['boolean'],
|
||||
help: 'Select true to maintain aspect ratio',
|
||||
default: false,
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
fn: (context, { shape, fill, border, borderWidth, maintainAspect }) => ({
|
||||
type: 'shape',
|
||||
shape,
|
||||
fill,
|
||||
border,
|
||||
borderWidth,
|
||||
maintainAspect,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,34 +6,36 @@
|
|||
|
||||
import { sortBy } from 'lodash';
|
||||
|
||||
export const sort = () => ({
|
||||
name: 'sort',
|
||||
type: 'datatable',
|
||||
help: 'Sorts a datatable on a column',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
by: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'column'],
|
||||
multi: false, // TODO: No reason you couldn't.
|
||||
help:
|
||||
'The column to sort on. If column is not specified, the datatable will be sorted on the first column.',
|
||||
export function sort() {
|
||||
return {
|
||||
name: 'sort',
|
||||
type: 'datatable',
|
||||
help: 'Sorts a datatable on a column',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
reverse: {
|
||||
types: ['boolean'],
|
||||
help:
|
||||
'Reverse the sort order. If reverse is not specified, the datatable will be sorted in ascending order.',
|
||||
options: [true, false],
|
||||
args: {
|
||||
by: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'column'],
|
||||
multi: false, // TODO: No reason you couldn't.
|
||||
help:
|
||||
'The column to sort on. If column is not specified, the datatable will be sorted on the first column.',
|
||||
},
|
||||
reverse: {
|
||||
types: ['boolean'],
|
||||
help:
|
||||
'Reverse the sort order. If reverse is not specified, the datatable will be sorted in ascending order.',
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const column = args.by || context.columns[0].name;
|
||||
fn: (context, args) => {
|
||||
const column = args.by || context.columns[0].name;
|
||||
|
||||
return {
|
||||
...context,
|
||||
rows: args.reverse ? sortBy(context.rows, column).reverse() : sortBy(context.rows, column),
|
||||
};
|
||||
},
|
||||
});
|
||||
return {
|
||||
...context,
|
||||
rows: args.reverse ? sortBy(context.rows, column).reverse() : sortBy(context.rows, column),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,44 +6,46 @@
|
|||
|
||||
import { getType } from '@kbn/interpreter/common';
|
||||
|
||||
export const staticColumn = () => ({
|
||||
name: 'staticColumn',
|
||||
type: 'datatable',
|
||||
help: 'Add a column with a static value',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
name: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'column'],
|
||||
help: 'The name of the new column column',
|
||||
required: true,
|
||||
export function staticColumn() {
|
||||
return {
|
||||
name: 'staticColumn',
|
||||
type: 'datatable',
|
||||
help: 'Add a column with a static value',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
value: {
|
||||
types: ['string', 'number', 'boolean', 'null'],
|
||||
help:
|
||||
'The value to insert in each column. Tip: use a sub-expression to rollup other columns into a static value',
|
||||
default: null,
|
||||
args: {
|
||||
name: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'column'],
|
||||
help: 'The name of the new column column',
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
types: ['string', 'number', 'boolean', 'null'],
|
||||
help:
|
||||
'The value to insert in each column. Tip: use a sub-expression to rollup other columns into a static value',
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const rows = context.rows.map(row => ({ ...row, [args.name]: args.value }));
|
||||
const type = getType(args.value);
|
||||
const columns = [...context.columns];
|
||||
const existingColumnIndex = columns.findIndex(({ name }) => name === args.name);
|
||||
const newColumn = { name: args.name, type };
|
||||
fn: (context, args) => {
|
||||
const rows = context.rows.map(row => ({ ...row, [args.name]: args.value }));
|
||||
const type = getType(args.value);
|
||||
const columns = [...context.columns];
|
||||
const existingColumnIndex = columns.findIndex(({ name }) => name === args.name);
|
||||
const newColumn = { name: args.name, type };
|
||||
|
||||
if (existingColumnIndex > -1) {
|
||||
columns[existingColumnIndex] = newColumn;
|
||||
} else {
|
||||
columns.push(newColumn);
|
||||
}
|
||||
if (existingColumnIndex > -1) {
|
||||
columns[existingColumnIndex] = newColumn;
|
||||
} else {
|
||||
columns.push(newColumn);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows,
|
||||
};
|
||||
},
|
||||
});
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,20 +4,22 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const string = () => ({
|
||||
name: 'string',
|
||||
aliases: [],
|
||||
type: 'string',
|
||||
help:
|
||||
'Output a string made of other strings. Mostly useful when combined with sub-expressions that output a string, ' +
|
||||
' or something castable to a string',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
multi: true,
|
||||
help: "One or more strings to join together. Don't forget spaces where needed!",
|
||||
export function string() {
|
||||
return {
|
||||
name: 'string',
|
||||
aliases: [],
|
||||
type: 'string',
|
||||
help:
|
||||
'Output a string made of other strings. Mostly useful when combined with sub-expressions that output a string, ' +
|
||||
' or something castable to a string',
|
||||
args: {
|
||||
value: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
multi: true,
|
||||
help: "One or more strings to join together. Don't forget spaces where needed!",
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => args.value.join(''),
|
||||
});
|
||||
fn: (context, args) => args.value.join(''),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,34 +4,36 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const switchFn = () => ({
|
||||
name: 'switch',
|
||||
help: 'Perform conditional logic with multiple conditions',
|
||||
args: {
|
||||
case: {
|
||||
types: ['case'],
|
||||
aliases: ['_'],
|
||||
resolve: false,
|
||||
multi: true,
|
||||
help: 'The list of conditions to check',
|
||||
export function switchFn() {
|
||||
return {
|
||||
name: 'switch',
|
||||
help: 'Perform conditional logic with multiple conditions',
|
||||
args: {
|
||||
case: {
|
||||
types: ['case'],
|
||||
aliases: ['_'],
|
||||
resolve: false,
|
||||
multi: true,
|
||||
help: 'The list of conditions to check',
|
||||
},
|
||||
default: {
|
||||
aliases: ['finally'],
|
||||
resolve: false,
|
||||
help: 'The default case if no cases match',
|
||||
},
|
||||
},
|
||||
default: {
|
||||
aliases: ['finally'],
|
||||
resolve: false,
|
||||
help: 'The default case if no cases match',
|
||||
},
|
||||
},
|
||||
fn: async (context, args) => {
|
||||
const cases = args.case || [];
|
||||
for (let i = 0; i < cases.length; i++) {
|
||||
const { matches, result } = await cases[i]();
|
||||
if (matches) {
|
||||
return result;
|
||||
fn: async (context, args) => {
|
||||
const cases = args.case || [];
|
||||
for (let i = 0; i < cases.length; i++) {
|
||||
const { matches, result } = await cases[i]();
|
||||
if (matches) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof args.default !== 'undefined') {
|
||||
return await args.default();
|
||||
}
|
||||
return context;
|
||||
},
|
||||
});
|
||||
if (typeof args.default !== 'undefined') {
|
||||
return await args.default();
|
||||
}
|
||||
return context;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,51 +4,54 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const table = () => ({
|
||||
name: 'table',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a Data Table element',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
font: {
|
||||
types: ['style'],
|
||||
default: '{font}',
|
||||
help: 'Font style',
|
||||
export function table() {
|
||||
return {
|
||||
name: 'table',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
help: 'Configure a Data Table element',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
paginate: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: 'Show pagination controls. If set to false only the first page will be displayed',
|
||||
options: [true, false],
|
||||
},
|
||||
perPage: {
|
||||
types: ['number'],
|
||||
default: 10,
|
||||
help: 'Show this many rows per page. You probably want to raise this is disabling pagination',
|
||||
},
|
||||
showHeader: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: 'Show or hide the header row with titles for each column',
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const { font, paginate, perPage, showHeader } = args;
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'table',
|
||||
value: {
|
||||
datatable: context,
|
||||
font,
|
||||
paginate,
|
||||
perPage,
|
||||
showHeader,
|
||||
args: {
|
||||
font: {
|
||||
types: ['style'],
|
||||
default: '{font}',
|
||||
help: 'Font style',
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
paginate: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: 'Show pagination controls. If set to false only the first page will be displayed',
|
||||
options: [true, false],
|
||||
},
|
||||
perPage: {
|
||||
types: ['number'],
|
||||
default: 10,
|
||||
help:
|
||||
'Show this many rows per page. You probably want to raise this is disabling pagination',
|
||||
},
|
||||
showHeader: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: 'Show or hide the header row with titles for each column',
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const { font, paginate, perPage, showHeader } = args;
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'table',
|
||||
value: {
|
||||
datatable: context,
|
||||
font,
|
||||
paginate,
|
||||
perPage,
|
||||
showHeader,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,23 +6,25 @@
|
|||
|
||||
import { takeRight } from 'lodash';
|
||||
|
||||
export const tail = () => ({
|
||||
name: 'tail',
|
||||
aliases: [],
|
||||
type: 'datatable',
|
||||
help: 'Get the last N rows from the end of a datatable. Also see `head`',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
count: {
|
||||
aliases: ['_'],
|
||||
types: ['number'],
|
||||
help: 'Return this many rows from the end of the datatable',
|
||||
export function tail() {
|
||||
return {
|
||||
name: 'tail',
|
||||
aliases: [],
|
||||
type: 'datatable',
|
||||
help: 'Get the last N rows from the end of a datatable. Also see `head`',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => ({
|
||||
...context,
|
||||
rows: takeRight(context.rows, args.count),
|
||||
}),
|
||||
});
|
||||
args: {
|
||||
count: {
|
||||
aliases: ['_'],
|
||||
types: ['number'],
|
||||
help: 'Return this many rows from the end of the datatable',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => ({
|
||||
...context,
|
||||
rows: takeRight(context.rows, args.count),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,63 +6,65 @@
|
|||
|
||||
import dateMath from '@elastic/datemath';
|
||||
|
||||
export const timefilter = () => ({
|
||||
name: 'timefilter',
|
||||
aliases: [],
|
||||
type: 'filter',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
help: 'Create a timefilter for querying a source',
|
||||
args: {
|
||||
column: {
|
||||
type: ['string'],
|
||||
aliases: ['field', 'c'],
|
||||
default: '@timestamp',
|
||||
help: 'The column or field to attach the filter to',
|
||||
export function timefilter() {
|
||||
return {
|
||||
name: 'timefilter',
|
||||
aliases: [],
|
||||
type: 'filter',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
from: {
|
||||
types: ['string', 'null'],
|
||||
aliases: ['f', 'start'],
|
||||
help: 'Beginning of the range, in ISO8601 or Elasticsearch datemath format',
|
||||
help: 'Create a timefilter for querying a source',
|
||||
args: {
|
||||
column: {
|
||||
type: ['string'],
|
||||
aliases: ['field', 'c'],
|
||||
default: '@timestamp',
|
||||
help: 'The column or field to attach the filter to',
|
||||
},
|
||||
from: {
|
||||
types: ['string', 'null'],
|
||||
aliases: ['f', 'start'],
|
||||
help: 'Beginning of the range, in ISO8601 or Elasticsearch datemath format',
|
||||
},
|
||||
to: {
|
||||
types: ['string', 'null'],
|
||||
aliases: ['t', 'end'],
|
||||
help: 'End of the range, in ISO8601 or Elasticsearch datemath format',
|
||||
},
|
||||
},
|
||||
to: {
|
||||
types: ['string', 'null'],
|
||||
aliases: ['t', 'end'],
|
||||
help: 'End of the range, in ISO8601 or Elasticsearch datemath format',
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
if (!args.from && !args.to) {
|
||||
return context;
|
||||
}
|
||||
|
||||
const { from, to, column } = args;
|
||||
const filter = {
|
||||
type: 'time',
|
||||
column,
|
||||
};
|
||||
|
||||
function parseAndValidate(str) {
|
||||
if (!str) {
|
||||
return;
|
||||
fn: (context, args) => {
|
||||
if (!args.from && !args.to) {
|
||||
return context;
|
||||
}
|
||||
|
||||
const moment = dateMath.parse(str);
|
||||
if (!moment || !moment.isValid()) {
|
||||
throw new Error(`Invalid date/time string: '${str}'`);
|
||||
const { from, to, column } = args;
|
||||
const filter = {
|
||||
type: 'time',
|
||||
column,
|
||||
};
|
||||
|
||||
function parseAndValidate(str) {
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
const moment = dateMath.parse(str);
|
||||
if (!moment || !moment.isValid()) {
|
||||
throw new Error(`Invalid date/time string: '${str}'`);
|
||||
}
|
||||
return moment.toISOString();
|
||||
}
|
||||
return moment.toISOString();
|
||||
}
|
||||
|
||||
if (to != null) {
|
||||
filter.to = parseAndValidate(to);
|
||||
}
|
||||
if (to != null) {
|
||||
filter.to = parseAndValidate(to);
|
||||
}
|
||||
|
||||
if (from != null) {
|
||||
filter.from = parseAndValidate(from);
|
||||
}
|
||||
if (from != null) {
|
||||
filter.from = parseAndValidate(from);
|
||||
}
|
||||
|
||||
return { ...context, and: [...context.and, filter] };
|
||||
},
|
||||
});
|
||||
return { ...context, and: [...context.and, filter] };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,32 +4,34 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const timefilterControl = () => ({
|
||||
name: 'timefilterControl',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
help: 'Configure a time filter control element',
|
||||
args: {
|
||||
column: {
|
||||
type: ['string'],
|
||||
aliases: ['field', 'c'],
|
||||
help: 'The column or field to attach the filter to',
|
||||
export function timefilterControl() {
|
||||
return {
|
||||
name: 'timefilterControl',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
context: {
|
||||
types: ['null'],
|
||||
},
|
||||
compact: {
|
||||
type: ['boolean'],
|
||||
help: 'Show the time filter as a button that triggers a popover',
|
||||
default: true,
|
||||
options: [true, false],
|
||||
help: 'Configure a time filter control element',
|
||||
args: {
|
||||
column: {
|
||||
type: ['string'],
|
||||
aliases: ['field', 'c'],
|
||||
help: 'The column or field to attach the filter to',
|
||||
},
|
||||
compact: {
|
||||
type: ['boolean'],
|
||||
help: 'Show the time filter as a button that triggers a popover',
|
||||
default: true,
|
||||
options: [true, false],
|
||||
},
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'time_filter',
|
||||
value: args,
|
||||
};
|
||||
},
|
||||
});
|
||||
fn: (context, args) => {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'time_filter',
|
||||
value: args,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,63 +8,65 @@ import { sortBy } from 'lodash';
|
|||
import { queryDatatable } from '../../../../common/lib/datatable/query';
|
||||
import { getDemoRows } from './get_demo_rows';
|
||||
|
||||
export const demodata = () => ({
|
||||
name: 'demodata',
|
||||
aliases: [],
|
||||
type: 'datatable',
|
||||
help: 'A mock data set that includes project CI times with usernames, countries and run phases',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
args: {
|
||||
type: {
|
||||
types: ['string', 'null'],
|
||||
aliases: ['_'],
|
||||
help: 'The name of the demo data set to use',
|
||||
default: 'ci',
|
||||
options: ['ci', 'shirts'],
|
||||
export function demodata() {
|
||||
return {
|
||||
name: 'demodata',
|
||||
aliases: [],
|
||||
type: 'datatable',
|
||||
help: 'A mock data set that includes project CI times with usernames, countries and run phases',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const demoRows = getDemoRows(args.type);
|
||||
|
||||
let set = {};
|
||||
if (args.type === 'ci') {
|
||||
set = {
|
||||
columns: [
|
||||
{ name: '@timestamp', type: 'date' },
|
||||
{ name: 'time', type: 'date' },
|
||||
{ name: 'cost', type: 'number' },
|
||||
{ name: 'username', type: 'string' },
|
||||
{ name: 'price', type: 'number' },
|
||||
{ name: 'age', type: 'number' },
|
||||
{ name: 'country', type: 'string' },
|
||||
{ name: 'state', type: 'string' },
|
||||
{ name: 'project', type: 'string' },
|
||||
{ name: 'percent_uptime', type: 'number' },
|
||||
],
|
||||
rows: sortBy(demoRows, 'time'),
|
||||
};
|
||||
} else if (args.type === 'shirts') {
|
||||
set = {
|
||||
columns: [
|
||||
{ name: 'size', type: 'string' },
|
||||
{ name: 'color', type: 'string' },
|
||||
{ name: 'price', type: 'number' },
|
||||
{ name: 'cut', type: 'string' },
|
||||
],
|
||||
rows: demoRows,
|
||||
};
|
||||
}
|
||||
|
||||
const { columns, rows } = set;
|
||||
return queryDatatable(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows,
|
||||
args: {
|
||||
type: {
|
||||
types: ['string', 'null'],
|
||||
aliases: ['_'],
|
||||
help: 'The name of the demo data set to use',
|
||||
default: 'ci',
|
||||
options: ['ci', 'shirts'],
|
||||
},
|
||||
context
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
fn: (context, args) => {
|
||||
const demoRows = getDemoRows(args.type);
|
||||
|
||||
let set = {};
|
||||
if (args.type === 'ci') {
|
||||
set = {
|
||||
columns: [
|
||||
{ name: '@timestamp', type: 'date' },
|
||||
{ name: 'time', type: 'date' },
|
||||
{ name: 'cost', type: 'number' },
|
||||
{ name: 'username', type: 'string' },
|
||||
{ name: 'price', type: 'number' },
|
||||
{ name: 'age', type: 'number' },
|
||||
{ name: 'country', type: 'string' },
|
||||
{ name: 'state', type: 'string' },
|
||||
{ name: 'project', type: 'string' },
|
||||
{ name: 'percent_uptime', type: 'number' },
|
||||
],
|
||||
rows: sortBy(demoRows, 'time'),
|
||||
};
|
||||
} else if (args.type === 'shirts') {
|
||||
set = {
|
||||
columns: [
|
||||
{ name: 'size', type: 'string' },
|
||||
{ name: 'color', type: 'string' },
|
||||
{ name: 'price', type: 'number' },
|
||||
{ name: 'cut', type: 'string' },
|
||||
],
|
||||
rows: demoRows,
|
||||
};
|
||||
}
|
||||
|
||||
const { columns, rows } = set;
|
||||
return queryDatatable(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns,
|
||||
rows,
|
||||
},
|
||||
context
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,48 +6,50 @@
|
|||
|
||||
import { buildESRequest } from '../../../server/lib/build_es_request';
|
||||
|
||||
export const escount = () => ({
|
||||
name: 'escount',
|
||||
type: 'number',
|
||||
help: 'Query elasticsearch for a count of the number of hits matching a query',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
args: {
|
||||
index: {
|
||||
types: ['string', 'null'],
|
||||
default: '_all',
|
||||
help: 'Specify an index pattern. Eg "logstash-*"',
|
||||
export function escount() {
|
||||
return {
|
||||
name: 'escount',
|
||||
type: 'number',
|
||||
help: 'Query elasticsearch for a count of the number of hits matching a query',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
query: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'q'],
|
||||
help: 'A Lucene query string',
|
||||
default: '"-_index:.kibana"',
|
||||
},
|
||||
},
|
||||
fn: (context, args, handlers) => {
|
||||
context.and = context.and.concat([
|
||||
{
|
||||
type: 'luceneQueryString',
|
||||
query: args.query,
|
||||
args: {
|
||||
index: {
|
||||
types: ['string', 'null'],
|
||||
default: '_all',
|
||||
help: 'Specify an index pattern. Eg "logstash-*"',
|
||||
},
|
||||
]);
|
||||
query: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'q'],
|
||||
help: 'A Lucene query string',
|
||||
default: '"-_index:.kibana"',
|
||||
},
|
||||
},
|
||||
fn: (context, args, handlers) => {
|
||||
context.and = context.and.concat([
|
||||
{
|
||||
type: 'luceneQueryString',
|
||||
query: args.query,
|
||||
},
|
||||
]);
|
||||
|
||||
const esRequest = buildESRequest(
|
||||
{
|
||||
index: args.index,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [{ match_all: {} }],
|
||||
const esRequest = buildESRequest(
|
||||
{
|
||||
index: args.index,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [{ match_all: {} }],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
context
|
||||
);
|
||||
context
|
||||
);
|
||||
|
||||
return handlers.elasticsearchClient('count', esRequest).then(resp => resp.count);
|
||||
},
|
||||
});
|
||||
return handlers.elasticsearchClient('count', esRequest).then(resp => resp.count);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,78 +7,80 @@
|
|||
import squel from 'squel';
|
||||
import { queryEsSQL } from '../../../server/lib/query_es_sql';
|
||||
|
||||
export const esdocs = () => ({
|
||||
name: 'esdocs',
|
||||
type: 'datatable',
|
||||
help:
|
||||
'Query elasticsearch and get back raw documents. We recommend you specify the fields you want, ' +
|
||||
'especially if you are going to ask for a lot of rows',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
args: {
|
||||
index: {
|
||||
types: ['string', 'null'],
|
||||
default: '_all',
|
||||
help: 'Specify an index pattern. Eg "logstash-*"',
|
||||
export function esdocs() {
|
||||
return {
|
||||
name: 'esdocs',
|
||||
type: 'datatable',
|
||||
help:
|
||||
'Query elasticsearch and get back raw documents. We recommend you specify the fields you want, ' +
|
||||
'especially if you are going to ask for a lot of rows',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
query: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'q'],
|
||||
help: 'A Lucene query string',
|
||||
default: '-_index:.kibana',
|
||||
},
|
||||
sort: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Sort directions as "field, direction". Eg "@timestamp, desc" or "bytes, asc"',
|
||||
},
|
||||
fields: {
|
||||
help: 'Comma separated list of fields. Fewer fields will perform better',
|
||||
types: ['string', 'null'],
|
||||
},
|
||||
metaFields: {
|
||||
help: 'Comma separated list of meta fields, eg "_index,_type"',
|
||||
types: ['string', 'null'],
|
||||
},
|
||||
count: {
|
||||
types: ['number'],
|
||||
default: 100,
|
||||
help: 'The number of docs to pull back. Smaller numbers perform better',
|
||||
},
|
||||
},
|
||||
fn: (context, args, handlers) => {
|
||||
context.and = context.and.concat([
|
||||
{
|
||||
type: 'luceneQueryString',
|
||||
query: args.query,
|
||||
args: {
|
||||
index: {
|
||||
types: ['string', 'null'],
|
||||
default: '_all',
|
||||
help: 'Specify an index pattern. Eg "logstash-*"',
|
||||
},
|
||||
]);
|
||||
query: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'q'],
|
||||
help: 'A Lucene query string',
|
||||
default: '-_index:.kibana',
|
||||
},
|
||||
sort: {
|
||||
types: ['string', 'null'],
|
||||
help: 'Sort directions as "field, direction". Eg "@timestamp, desc" or "bytes, asc"',
|
||||
},
|
||||
fields: {
|
||||
help: 'Comma separated list of fields. Fewer fields will perform better',
|
||||
types: ['string', 'null'],
|
||||
},
|
||||
metaFields: {
|
||||
help: 'Comma separated list of meta fields, eg "_index,_type"',
|
||||
types: ['string', 'null'],
|
||||
},
|
||||
count: {
|
||||
types: ['number'],
|
||||
default: 100,
|
||||
help: 'The number of docs to pull back. Smaller numbers perform better',
|
||||
},
|
||||
},
|
||||
fn: (context, args, handlers) => {
|
||||
context.and = context.and.concat([
|
||||
{
|
||||
type: 'luceneQueryString',
|
||||
query: args.query,
|
||||
},
|
||||
]);
|
||||
|
||||
let query = squel
|
||||
.select({
|
||||
autoQuoteTableNames: true,
|
||||
autoQuoteFieldNames: true,
|
||||
autoQuoteAliasNames: true,
|
||||
nameQuoteCharacter: '"',
|
||||
})
|
||||
.from(args.index.toLowerCase());
|
||||
let query = squel
|
||||
.select({
|
||||
autoQuoteTableNames: true,
|
||||
autoQuoteFieldNames: true,
|
||||
autoQuoteAliasNames: true,
|
||||
nameQuoteCharacter: '"',
|
||||
})
|
||||
.from(args.index.toLowerCase());
|
||||
|
||||
if (args.fields) {
|
||||
const fields = args.fields.split(',').map(field => field.trim());
|
||||
fields.forEach(field => (query = query.field(field)));
|
||||
}
|
||||
|
||||
if (args.sort) {
|
||||
const [sortField, sortOrder] = args.sort.split(',').map(str => str.trim());
|
||||
if (sortField) {
|
||||
query.order(`"${sortField}"`, sortOrder.toLowerCase() === 'asc');
|
||||
if (args.fields) {
|
||||
const fields = args.fields.split(',').map(field => field.trim());
|
||||
fields.forEach(field => (query = query.field(field)));
|
||||
}
|
||||
}
|
||||
|
||||
return queryEsSQL(handlers.elasticsearchClient, {
|
||||
count: args.count,
|
||||
query: query.toString(),
|
||||
filter: context.and,
|
||||
});
|
||||
},
|
||||
});
|
||||
if (args.sort) {
|
||||
const [sortField, sortOrder] = args.sort.split(',').map(str => str.trim());
|
||||
if (sortField) {
|
||||
query.order(`"${sortField}"`, sortOrder.toLowerCase() === 'asc');
|
||||
}
|
||||
}
|
||||
|
||||
return queryEsSQL(handlers.elasticsearchClient, {
|
||||
count: args.count,
|
||||
query: query.toString(),
|
||||
filter: context.and,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,30 +6,32 @@
|
|||
|
||||
import { queryEsSQL } from '../../../server/lib/query_es_sql';
|
||||
|
||||
export const essql = () => ({
|
||||
name: 'essql',
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
help: 'Elasticsearch SQL',
|
||||
args: {
|
||||
query: {
|
||||
aliases: ['_', 'q'],
|
||||
types: ['string'],
|
||||
help: 'SQL query',
|
||||
export function essql() {
|
||||
return {
|
||||
name: 'essql',
|
||||
type: 'datatable',
|
||||
context: {
|
||||
types: ['filter'],
|
||||
},
|
||||
count: {
|
||||
types: ['number'],
|
||||
default: 1000,
|
||||
help: 'Elasticsearch SQL',
|
||||
args: {
|
||||
query: {
|
||||
aliases: ['_', 'q'],
|
||||
types: ['string'],
|
||||
help: 'SQL query',
|
||||
},
|
||||
count: {
|
||||
types: ['number'],
|
||||
default: 1000,
|
||||
},
|
||||
timezone: {
|
||||
aliases: ['tz'],
|
||||
types: ['string'],
|
||||
default: 'UTC',
|
||||
help: 'Timezone to use for date operations, valid ISO formats and UTC offsets both work',
|
||||
},
|
||||
},
|
||||
timezone: {
|
||||
aliases: ['tz'],
|
||||
types: ['string'],
|
||||
default: 'UTC',
|
||||
help: 'Timezone to use for date operations, valid ISO formats and UTC offsets both work',
|
||||
},
|
||||
},
|
||||
fn: (context, args, handlers) =>
|
||||
queryEsSQL(handlers.elasticsearchClient, { ...args, filter: context.and }),
|
||||
});
|
||||
fn: (context, args, handlers) =>
|
||||
queryEsSQL(handlers.elasticsearchClient, { ...args, filter: context.and }),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,166 +17,170 @@ import { getExpressionType } from './lib/get_expression_type';
|
|||
|
||||
const columnExists = (cols, colName) => cols.includes(unquoteString(colName));
|
||||
|
||||
export const pointseries = () => ({
|
||||
name: 'pointseries',
|
||||
type: 'pointseries',
|
||||
help:
|
||||
'Turn a datatable into a point series model. Currently we differentiate measure from dimensions by looking for a [TinyMath function](https://www.elastic.co/guide/en/kibana/current/canvas-tinymath-functions.html). ' +
|
||||
'If you enter a TinyMath expression in your argument, we treat that argument as a measure, otherwise it is a dimension. Dimensions are combined to create unique ' +
|
||||
'keys. Measures are then deduplicated by those keys using the specified TinyMath function',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
args: {
|
||||
x: {
|
||||
types: ['string', 'null'],
|
||||
help: 'The values along the X-axis',
|
||||
export function pointseries() {
|
||||
return {
|
||||
name: 'pointseries',
|
||||
type: 'pointseries',
|
||||
help:
|
||||
'Turn a datatable into a point series model. Currently we differentiate measure from dimensions by looking for a [TinyMath function](https://www.elastic.co/guide/en/kibana/current/canvas-tinymath-functions.html). ' +
|
||||
'If you enter a TinyMath expression in your argument, we treat that argument as a measure, otherwise it is a dimension. Dimensions are combined to create unique ' +
|
||||
'keys. Measures are then deduplicated by those keys using the specified TinyMath function',
|
||||
context: {
|
||||
types: ['datatable'],
|
||||
},
|
||||
y: {
|
||||
types: ['string', 'null'],
|
||||
help: 'The values along the y-axis',
|
||||
args: {
|
||||
x: {
|
||||
types: ['string', 'null'],
|
||||
help: 'The values along the X-axis',
|
||||
},
|
||||
y: {
|
||||
types: ['string', 'null'],
|
||||
help: 'The values along the y-axis',
|
||||
},
|
||||
color: {
|
||||
types: ['string', 'null'],
|
||||
help: "An expression to use in determining the mark's color", // If you need categorization, transform the field.
|
||||
},
|
||||
size: {
|
||||
types: ['string', 'null'],
|
||||
help: 'For elements that support it, the size of the marks',
|
||||
},
|
||||
text: {
|
||||
types: ['string', 'null'],
|
||||
help: 'For use in charts that support it, the text to show in the mark',
|
||||
},
|
||||
// In the future it may make sense to add things like shape, or tooltip values, but I think what we have is good for now
|
||||
// The way the function below is written you can add as many arbitrary named args as you want.
|
||||
},
|
||||
color: {
|
||||
types: ['string', 'null'],
|
||||
help: "An expression to use in determining the mark's color", // If you need categorization, transform the field.
|
||||
},
|
||||
size: {
|
||||
types: ['string', 'null'],
|
||||
help: 'For elements that support it, the size of the marks',
|
||||
},
|
||||
text: {
|
||||
types: ['string', 'null'],
|
||||
help: 'For use in charts that support it, the text to show in the mark',
|
||||
},
|
||||
// In the future it may make sense to add things like shape, or tooltip values, but I think what we have is good for now
|
||||
// The way the function below is written you can add as many arbitrary named args as you want.
|
||||
},
|
||||
fn: (context, args) => {
|
||||
// Note: can't replace pivotObjectArray with datatableToMathContext, lose name of non-numeric columns
|
||||
const columnNames = context.columns.map(col => col.name);
|
||||
const mathScope = pivotObjectArray(context.rows, columnNames);
|
||||
const autoQuoteColumn = col => {
|
||||
if (!columnNames.includes(col)) {
|
||||
return col;
|
||||
}
|
||||
return col.match(/\s/) ? `'${col}'` : col;
|
||||
};
|
||||
|
||||
const measureNames = [];
|
||||
const dimensions = [];
|
||||
const columns = {};
|
||||
|
||||
// Separates args into dimensions and measures arrays
|
||||
// by checking if arg is a column reference (dimension)
|
||||
Object.keys(args).forEach(arg => {
|
||||
const mathExp = autoQuoteColumn(args[arg]);
|
||||
|
||||
if (mathExp != null && mathExp.trim() !== '') {
|
||||
const col = {
|
||||
type: '',
|
||||
role: '',
|
||||
expression: mathExp,
|
||||
};
|
||||
|
||||
if (isColumnReference(mathExp)) {
|
||||
// TODO: Do something better if the column does not exist
|
||||
if (!columnExists(columnNames, mathExp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dimensions.push({
|
||||
name: arg,
|
||||
value: mathExp,
|
||||
});
|
||||
col.type = getExpressionType(context.columns, mathExp);
|
||||
col.role = 'dimension';
|
||||
} else {
|
||||
measureNames.push(arg);
|
||||
col.type = 'number';
|
||||
col.role = 'measure';
|
||||
fn: (context, args) => {
|
||||
// Note: can't replace pivotObjectArray with datatableToMathContext, lose name of non-numeric columns
|
||||
const columnNames = context.columns.map(col => col.name);
|
||||
const mathScope = pivotObjectArray(context.rows, columnNames);
|
||||
const autoQuoteColumn = col => {
|
||||
if (!columnNames.includes(col)) {
|
||||
return col;
|
||||
}
|
||||
return col.match(/\s/) ? `'${col}'` : col;
|
||||
};
|
||||
|
||||
columns[arg] = col;
|
||||
}
|
||||
});
|
||||
const measureNames = [];
|
||||
const dimensions = [];
|
||||
const columns = {};
|
||||
|
||||
const PRIMARY_KEY = '%%CANVAS_POINTSERIES_PRIMARY_KEY%%';
|
||||
const rows = context.rows.map((row, i) => ({ ...row, [PRIMARY_KEY]: i }));
|
||||
// Separates args into dimensions and measures arrays
|
||||
// by checking if arg is a column reference (dimension)
|
||||
Object.keys(args).forEach(arg => {
|
||||
const mathExp = autoQuoteColumn(args[arg]);
|
||||
|
||||
function normalizeValue(expression, value) {
|
||||
switch (getExpressionType(context.columns, expression)) {
|
||||
case 'string':
|
||||
return String(value);
|
||||
case 'number':
|
||||
return Number(value);
|
||||
case 'date':
|
||||
return moment(value).valueOf();
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
if (mathExp != null && mathExp.trim() !== '') {
|
||||
const col = {
|
||||
type: '',
|
||||
role: '',
|
||||
expression: mathExp,
|
||||
};
|
||||
|
||||
// Dimensions
|
||||
// Group rows by their dimension values, using the argument values and preserving the PRIMARY_KEY
|
||||
// There's probably a better way to do this
|
||||
const results = rows.reduce((acc, row, i) => {
|
||||
const newRow = dimensions.reduce(
|
||||
(acc, { name, value }) => {
|
||||
try {
|
||||
acc[name] = args[name] ? normalizeValue(value, evaluate(value, mathScope)[i]) : '_all';
|
||||
} catch (e) {
|
||||
// TODO: handle invalid column names...
|
||||
// Do nothing if column does not exist
|
||||
// acc[dimension] = '_all';
|
||||
if (isColumnReference(mathExp)) {
|
||||
// TODO: Do something better if the column does not exist
|
||||
if (!columnExists(columnNames, mathExp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dimensions.push({
|
||||
name: arg,
|
||||
value: mathExp,
|
||||
});
|
||||
col.type = getExpressionType(context.columns, mathExp);
|
||||
col.role = 'dimension';
|
||||
} else {
|
||||
measureNames.push(arg);
|
||||
col.type = 'number';
|
||||
col.role = 'measure';
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ [PRIMARY_KEY]: row[PRIMARY_KEY] }
|
||||
|
||||
columns[arg] = col;
|
||||
}
|
||||
});
|
||||
|
||||
const PRIMARY_KEY = '%%CANVAS_POINTSERIES_PRIMARY_KEY%%';
|
||||
const rows = context.rows.map((row, i) => ({ ...row, [PRIMARY_KEY]: i }));
|
||||
|
||||
function normalizeValue(expression, value) {
|
||||
switch (getExpressionType(context.columns, expression)) {
|
||||
case 'string':
|
||||
return String(value);
|
||||
case 'number':
|
||||
return Number(value);
|
||||
case 'date':
|
||||
return moment(value).valueOf();
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// Dimensions
|
||||
// Group rows by their dimension values, using the argument values and preserving the PRIMARY_KEY
|
||||
// There's probably a better way to do this
|
||||
const results = rows.reduce((acc, row, i) => {
|
||||
const newRow = dimensions.reduce(
|
||||
(acc, { name, value }) => {
|
||||
try {
|
||||
acc[name] = args[name]
|
||||
? normalizeValue(value, evaluate(value, mathScope)[i])
|
||||
: '_all';
|
||||
} catch (e) {
|
||||
// TODO: handle invalid column names...
|
||||
// Do nothing if column does not exist
|
||||
// acc[dimension] = '_all';
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ [PRIMARY_KEY]: row[PRIMARY_KEY] }
|
||||
);
|
||||
|
||||
return Object.assign(acc, { [row[PRIMARY_KEY]]: newRow });
|
||||
}, {});
|
||||
|
||||
// Measures
|
||||
// First group up all of the distinct dimensioned bits. Each of these will be reduced to just 1 value
|
||||
// for each measure
|
||||
const measureKeys = groupBy(rows, row =>
|
||||
dimensions.map(({ name }) => (args[name] ? row[args[name]] : '_all')).join('::%BURLAP%::')
|
||||
);
|
||||
|
||||
return Object.assign(acc, { [row[PRIMARY_KEY]]: newRow });
|
||||
}, {});
|
||||
// Then compute that 1 value for each measure
|
||||
values(measureKeys).forEach(rows => {
|
||||
const subtable = { type: 'datatable', columns: context.columns, rows: rows };
|
||||
const subScope = pivotObjectArray(subtable.rows, subtable.columns.map(col => col.name));
|
||||
const measureValues = measureNames.map(measure => {
|
||||
try {
|
||||
const ev = evaluate(args[measure], subScope);
|
||||
if (Array.isArray(ev)) {
|
||||
throw new Error('Expressions must be wrapped in a function such as sum()');
|
||||
}
|
||||
|
||||
// Measures
|
||||
// First group up all of the distinct dimensioned bits. Each of these will be reduced to just 1 value
|
||||
// for each measure
|
||||
const measureKeys = groupBy(rows, row =>
|
||||
dimensions.map(({ name }) => (args[name] ? row[args[name]] : '_all')).join('::%BURLAP%::')
|
||||
);
|
||||
|
||||
// Then compute that 1 value for each measure
|
||||
values(measureKeys).forEach(rows => {
|
||||
const subtable = { type: 'datatable', columns: context.columns, rows: rows };
|
||||
const subScope = pivotObjectArray(subtable.rows, subtable.columns.map(col => col.name));
|
||||
const measureValues = measureNames.map(measure => {
|
||||
try {
|
||||
const ev = evaluate(args[measure], subScope);
|
||||
if (Array.isArray(ev)) {
|
||||
throw new Error('Expressions must be wrapped in a function such as sum()');
|
||||
return ev;
|
||||
} catch (e) {
|
||||
// TODO: don't catch if eval to Array
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return ev;
|
||||
} catch (e) {
|
||||
// TODO: don't catch if eval to Array
|
||||
return null;
|
||||
}
|
||||
rows.forEach(row => {
|
||||
Object.assign(results[row[PRIMARY_KEY]], zipObject(measureNames, measureValues));
|
||||
});
|
||||
});
|
||||
|
||||
rows.forEach(row => {
|
||||
Object.assign(results[row[PRIMARY_KEY]], zipObject(measureNames, measureValues));
|
||||
});
|
||||
});
|
||||
// It only makes sense to uniq the rows in a point series as 2 values can not exist in the exact same place at the same time.
|
||||
const resultingRows = uniqBy(
|
||||
values(results).map(row => omit(row, PRIMARY_KEY)),
|
||||
JSON.stringify
|
||||
);
|
||||
|
||||
// It only makes sense to uniq the rows in a point series as 2 values can not exist in the exact same place at the same time.
|
||||
const resultingRows = uniqBy(
|
||||
values(results).map(row => omit(row, PRIMARY_KEY)),
|
||||
JSON.stringify
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'pointseries',
|
||||
columns: columns,
|
||||
rows: resultingRows,
|
||||
};
|
||||
},
|
||||
});
|
||||
return {
|
||||
type: 'pointseries',
|
||||
columns: columns,
|
||||
rows: resultingRows,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const server = () => ({
|
||||
name: 'server',
|
||||
help: 'Force the interpreter to return to the server',
|
||||
args: {},
|
||||
fn: context => context,
|
||||
});
|
||||
export function server() {
|
||||
return {
|
||||
name: 'server',
|
||||
help: 'Force the interpreter to return to the server',
|
||||
args: {},
|
||||
fn: context => context,
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue