WIP move / change shape of body function

pull/10616/head
Andrew Watkins 2018-04-23 15:27:19 -07:00
parent 292d04e5a8
commit f3116aaab0
11 changed files with 874 additions and 147 deletions

View File

@ -1,5 +1,6 @@
// Texas Ranger
import _ from 'lodash'
import {FlatBody, Func} from 'src/types/ifql'
interface Expression {
argument: object
@ -19,14 +20,9 @@ interface Body {
}
interface FlatExpression {
type: string
source: string
funcs: FuncNode[]
}
interface FuncNode {
name: string
arguments: any[]
source: string
funcs: Func[]
}
interface AST {
@ -44,7 +40,7 @@ export default class Walker {
return this.buildFuncNodes(this.walk(this.baseExpression))
}
public get stuff() {
public get body(): FlatBody[] {
const body = _.get(this.ast, 'body', new Array<Body>())
return body.map(b => {
if (b.type.includes('Expression')) {
@ -74,6 +70,7 @@ export default class Walker {
const funcs = this.buildFuncNodes(this.walk(expression))
return {
type: expression.type,
source: location.source,
funcs,
}
@ -86,6 +83,7 @@ export default class Walker {
const funcs = this.buildFuncNodes(this.walk(expression))
return {
type: expression.type,
source: location.source,
funcs,
}
@ -122,11 +120,11 @@ export default class Walker {
return [{name, args, source}]
}
private buildFuncNodes = (nodes): FuncNode[] => {
private buildFuncNodes = (nodes): Func[] => {
return nodes.map(({name, args, source}) => {
return {
name,
arguments: this.reduceArgs(args),
args: this.reduceArgs(args),
source,
}
})

View File

@ -0,0 +1,101 @@
import React, {PureComponent} from 'react'
import _ from 'lodash'
import FuncSelector from 'src/ifql/components/FuncSelector'
import FuncNode from 'src/ifql/components/FuncNode'
import {
FlatBody,
OnAddNode,
Suggestion,
OnChangeArg,
OnDeleteFuncNode,
} from 'src/types/ifql'
interface Props {
body: Body[]
onAddNode: OnAddNode
onChangeArg: OnChangeArg
onDeleteFuncNode: OnDeleteFuncNode
suggestions: Suggestion[]
onGenerateScript: () => void
}
interface Body extends FlatBody {
id: string
}
class BodyBuilder extends PureComponent<Props> {
public render() {
const {
body,
onAddNode,
onChangeArg,
onDeleteFuncNode,
onGenerateScript,
} = this.props
const bodybuilder = body.map(b => {
if (b.declarations.length) {
return b.declarations.map(d => {
if (d.funcs) {
return (
<div key={d.id} className="func-nodes-container">
<h4>
{d.name}
<FuncSelector
expressionID={d.id}
funcs={this.funcNames}
onAddNode={onAddNode}
/>
</h4>
{d.funcs.map(func => (
<FuncNode
key={func.id}
func={func}
expressionID={func.id}
onChangeArg={onChangeArg}
onDelete={onDeleteFuncNode}
onGenerateScript={onGenerateScript}
/>
))}
</div>
)
}
return <div key={b.id}>{b.source}</div>
})
}
return (
<div key={b.id} className="func-nodes-container">
<h4>
Expression
<FuncSelector
expressionID={b.id}
funcs={this.funcNames}
onAddNode={onAddNode}
/>
</h4>
{b.funcs.map(func => (
<FuncNode
key={func.id}
func={func}
expressionID={b.id}
onChangeArg={onChangeArg}
onDelete={onDeleteFuncNode}
onGenerateScript={onGenerateScript}
/>
))}
</div>
)
})
return _.flatten(bodybuilder)
}
private get funcNames() {
return this.props.suggestions.map(f => f.name)
}
}
export default BodyBuilder

View File

@ -2,21 +2,7 @@ import React, {PureComponent} from 'react'
import FuncArg from 'src/ifql/components/FuncArg'
import {OnChangeArg} from 'src/types/ifql'
import {ErrorHandling} from 'src/shared/decorators/errors'
type Value = string | boolean
interface Arg {
key: string
value: Value
type: string
}
export interface Func {
name: string
args: Arg[]
source: string
id: string
}
import {Func} from 'src/types/ifql'
interface Props {
func: Func
@ -30,6 +16,10 @@ export default class FuncArgs extends PureComponent<Props> {
public render() {
const {expressionID, func, onChangeArg, onGenerateScript} = this.props
if (!func.args) {
debugger
}
return (
<div className="func-args">
{func.args.map(({key, value, type}) => {

View File

@ -1,28 +1,20 @@
import React, {PureComponent} from 'react'
import FuncSelector from 'src/ifql/components/FuncSelector'
import FuncNode from 'src/ifql/components/FuncNode'
import BodyBuilder from 'src/ifql/components/BodyBuilder'
import TimeMachineEditor from 'src/ifql/components/TimeMachineEditor'
import {Func} from 'src/ifql/components/FuncArgs'
import {OnChangeArg, OnDeleteFuncNode, OnAddNode} from 'src/types/ifql'
import {
FlatBody,
Suggestion,
OnChangeArg,
OnDeleteFuncNode,
OnAddNode,
} from 'src/types/ifql'
import {ErrorHandling} from 'src/shared/decorators/errors'
export interface Suggestion {
name: string
params: {
[key: string]: string
}
}
interface Expression {
id: string
funcs: Func[]
}
interface Props {
script: string
suggestions: Suggestion[]
expressions: Expression[]
body: Body[]
onSubmitScript: () => void
onChangeScript: (script: string) => void
onAddNode: OnAddNode
@ -31,18 +23,23 @@ interface Props {
onGenerateScript: () => void
}
interface Body extends FlatBody {
id: string
}
@ErrorHandling
class TimeMachine extends PureComponent<Props> {
public render() {
const {
body,
script,
onAddNode,
expressions,
onChangeArg,
onChangeScript,
onSubmitScript,
onDeleteFuncNode,
onGenerateScript,
suggestions,
} = this.props
return (
@ -53,38 +50,18 @@ class TimeMachine extends PureComponent<Props> {
onSubmitScript={onSubmitScript}
/>
<div className="expression-container">
{expressions.map(({funcs, id}, i) => {
return (
<div key={id} className="func-nodes-container">
<h4>
Expression {i}
<FuncSelector
expressionID={id}
funcs={this.funcNames}
onAddNode={onAddNode}
/>
</h4>
{funcs.map(func => (
<FuncNode
key={func.id}
func={func}
expressionID={id}
onChangeArg={onChangeArg}
onDelete={onDeleteFuncNode}
onGenerateScript={onGenerateScript}
/>
))}
</div>
)
})}
<BodyBuilder
body={body}
onAddNode={onAddNode}
onChangeArg={onChangeArg}
onDeleteFuncNode={onDeleteFuncNode}
onGenerateScript={onGenerateScript}
suggestions={suggestions}
/>
</div>
</div>
)
}
private get funcNames() {
return this.props.suggestions.map(f => f.name)
}
}
export default TimeMachine

View File

@ -4,12 +4,13 @@ import {connect} from 'react-redux'
import uuid from 'uuid'
import _ from 'lodash'
import TimeMachine, {Suggestion} from 'src/ifql/components/TimeMachine'
import TimeMachine from 'src/ifql/components/TimeMachine'
import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
import Walker from 'src/ifql/ast/walker'
import {Func} from 'src/ifql/components/FuncArgs'
import {Func, Suggestion, FlatBody} from 'src/types/ifql'
import {InputArg} from 'src/types/ifql'
import {bodyNodes} from 'src/ifql/helpers'
import {getSuggestions, getAST} from 'src/ifql/apis'
import * as argTypes from 'src/ifql/constants/argumentTypes'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -24,17 +25,15 @@ interface Props {
links: Links
}
interface State {
suggestions: Suggestion[]
expressions: Expression[]
ast: object
script: string
interface Body extends FlatBody {
id: string
}
interface Expression {
id: string
funcs: Func[]
source: string
interface State {
body: Body[]
ast: object
script: string
suggestions: Suggestion[]
}
@ErrorHandling
@ -42,11 +41,11 @@ export class IFQLPage extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
suggestions: [],
expressions: [],
body: [],
ast: null,
suggestions: [],
script:
'from(db: "telegraf")\n\t|> filter() \n\t|> range(start: -15m)\n\nfrom(db: "telegraf")\n\t|> filter() \n\t|> range(start: -15m)\n\n',
'foo = from(db: "telegraf")\n\t|> filter() \n\t|> range(start: -15m)\n\nfrom(db: "telegraf")\n\t|> filter() \n\t|> range(start: -15m)\n\n',
}
}
@ -80,7 +79,7 @@ export class IFQLPage extends PureComponent<Props, State> {
<div className="container-fluid">
<TimeMachine
script={script}
expressions={this.state.expressions}
body={this.state.body}
suggestions={suggestions}
onAddNode={this.handleAddNode}
onChangeArg={this.handleChangeArg}
@ -221,61 +220,13 @@ export class IFQLPage extends PureComponent<Props, State> {
this.getASTResponse(script)
}
private expressions = (ast, suggestions): Expression[] => {
if (!ast) {
return []
}
const walker = new Walker(ast)
const expressions = walker.expressions.map(({funcs, source}) => {
const id = uuid.v4()
return {
id,
funcs: this.functions(funcs, suggestions),
source,
}
})
return expressions
}
private functions = (funcs, suggestions): Func[] => {
const functions = funcs.map(func => {
const {params, name} = suggestions.find(f => f.name === func.name)
const args = Object.entries(params).map(([key, type]) => {
const value = _.get(
func.arguments.find(arg => arg.key === key),
'value',
''
)
return {
key,
value,
type,
}
})
return {
id: uuid.v4(),
source: func.source,
name,
args,
}
})
return functions
}
private getASTResponse = async (script: string) => {
const {links} = this.props
try {
const ast = await getAST({url: links.ast, body: script})
const expressions = this.expressions(ast, this.state.suggestions)
this.setState({ast, script, expressions})
const body = bodyNodes(ast, this.state.suggestions)
this.setState({ast, script, body})
} catch (error) {
console.error('Could not parse AST', error)
}

View File

@ -0,0 +1,72 @@
import uuid from 'uuid'
import _ from 'lodash'
import Walker from 'src/ifql/ast/walker'
import {FlatBody, Func} from 'src/types/ifql'
interface Body extends FlatBody {
id: string
}
export const bodyNodes = (ast, suggestions): Body[] => {
if (!ast) {
return []
}
const walker = new Walker(ast)
const body = walker.body.map(b => {
const {type} = b
const id = uuid.v4()
if (type.includes('Variable')) {
const declarations = b.declarations.map(d => {
if (!d.funcs) {
return {...d, id: uuid.v4()}
}
return {
...d,
id: uuid.v4(),
funcs: functions(d.funcs, suggestions),
}
})
return {...b, type, id, declarations}
}
const {funcs, source} = b
return {
id,
funcs: functions(funcs, suggestions),
declarations: [],
type,
source,
}
})
return body
}
const functions = (funcs, suggestions): Func[] => {
const funcList = funcs.map(func => {
const {params, name} = suggestions.find(f => f.name === func.name)
const args = Object.entries(params).map(([key, type]) => {
const value = _.get(func.args.find(arg => arg.key === key), 'value', '')
return {
key,
value,
type,
}
})
return {
id: uuid.v4(),
source: func.source,
name,
args,
}
})
return funcList
}

View File

@ -9,3 +9,46 @@ export interface InputArg {
value: string | boolean
generate?: boolean
}
// Flattened AST
export interface FlatBody {
type: string
source: string
funcs?: Func[]
declarations?: FlatDeclaration[]
}
export interface Func {
type: string
name: string
args: Arg[]
source: string
id: string
}
type Value = string | boolean
interface Arg {
key: string
value: Value
type: string
}
interface FlatExpression {
id: string
funcs?: Func[]
}
interface FlatDeclaration extends FlatExpression {
name: string
value: string
type: string
}
// Semantic Graph list of available functions for ifql queries
export interface Suggestion {
name: string
params: {
[key: string]: string
}
}

View File

@ -0,0 +1,352 @@
export default {
type: 'Program',
location: {
start: {line: 1, column: 1},
end: {line: 1, column: 129},
source:
'literal = "foo"\n\ntele = from(db: "telegraf")\n\t|\u003e range(start: -15m)\n\nfrom(db: "telegraf")\n\t|\u003e filter() \n\t|\u003e range(start: -15m)\n\n',
},
body: [
{
type: 'VariableDeclaration',
location: {
start: {line: 1, column: 1},
end: {line: 1, column: 16},
source: 'literal = "foo"',
},
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
location: {
start: {line: 1, column: 1},
end: {line: 1, column: 8},
source: 'literal',
},
name: 'literal',
},
init: {
type: 'StringLiteral',
location: {
start: {line: 1, column: 11},
end: {line: 1, column: 16},
source: '"foo"',
},
value: 'foo',
},
},
],
},
{
type: 'VariableDeclaration',
location: {
start: {line: 3, column: 1},
end: {line: 3, column: 53},
source: 'tele = from(db: "telegraf")\n\t|\u003e range(start: -15m)\n\n',
},
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
location: {
start: {line: 3, column: 1},
end: {line: 3, column: 5},
source: 'tele',
},
name: 'tele',
},
init: {
type: 'PipeExpression',
location: {
start: {line: 4, column: 2},
end: {line: 4, column: 23},
source: '|\u003e range(start: -15m)',
},
argument: {
type: 'CallExpression',
location: {
start: {line: 3, column: 8},
end: {line: 3, column: 28},
source: 'from(db: "telegraf")',
},
callee: {
type: 'Identifier',
location: {
start: {line: 3, column: 8},
end: {line: 3, column: 12},
source: 'from',
},
name: 'from',
},
arguments: [
{
type: 'ObjectExpression',
location: {
start: {line: 3, column: 13},
end: {line: 3, column: 27},
source: 'db: "telegraf"',
},
properties: [
{
type: 'Property',
location: {
start: {line: 3, column: 13},
end: {line: 3, column: 27},
source: 'db: "telegraf"',
},
key: {
type: 'Identifier',
location: {
start: {line: 3, column: 13},
end: {line: 3, column: 15},
source: 'db',
},
name: 'db',
},
value: {
type: 'StringLiteral',
location: {
start: {line: 3, column: 17},
end: {line: 3, column: 27},
source: '"telegraf"',
},
value: 'telegraf',
},
},
],
},
],
},
call: {
type: 'CallExpression',
location: {
start: {line: 4, column: 5},
end: {line: 4, column: 23},
source: 'range(start: -15m)',
},
callee: {
type: 'Identifier',
location: {
start: {line: 4, column: 5},
end: {line: 4, column: 10},
source: 'range',
},
name: 'range',
},
arguments: [
{
type: 'ObjectExpression',
location: {
start: {line: 4, column: 11},
end: {line: 4, column: 22},
source: 'start: -15m',
},
properties: [
{
type: 'Property',
location: {
start: {line: 4, column: 11},
end: {line: 4, column: 22},
source: 'start: -15m',
},
key: {
type: 'Identifier',
location: {
start: {line: 4, column: 11},
end: {line: 4, column: 16},
source: 'start',
},
name: 'start',
},
value: {
type: 'UnaryExpression',
location: {
start: {line: 4, column: 18},
end: {line: 4, column: 22},
source: '-15m',
},
operator: '-',
argument: {
type: 'DurationLiteral',
location: {
start: {line: 4, column: 19},
end: {line: 4, column: 22},
source: '15m',
},
value: '15m0s',
},
},
},
],
},
],
},
},
},
],
},
{
type: 'ExpressionStatement',
location: {
start: {line: 6, column: 1},
end: {line: 6, column: 60},
source:
'from(db: "telegraf")\n\t|\u003e filter() \n\t|\u003e range(start: -15m)\n\n',
},
expression: {
type: 'PipeExpression',
location: {
start: {line: 8, column: 2},
end: {line: 8, column: 23},
source: '|\u003e range(start: -15m)',
},
argument: {
type: 'PipeExpression',
location: {
start: {line: 7, column: 2},
end: {line: 7, column: 13},
source: '|\u003e filter()',
},
argument: {
type: 'CallExpression',
location: {
start: {line: 6, column: 1},
end: {line: 6, column: 21},
source: 'from(db: "telegraf")',
},
callee: {
type: 'Identifier',
location: {
start: {line: 6, column: 1},
end: {line: 6, column: 5},
source: 'from',
},
name: 'from',
},
arguments: [
{
type: 'ObjectExpression',
location: {
start: {line: 6, column: 6},
end: {line: 6, column: 20},
source: 'db: "telegraf"',
},
properties: [
{
type: 'Property',
location: {
start: {line: 6, column: 6},
end: {line: 6, column: 20},
source: 'db: "telegraf"',
},
key: {
type: 'Identifier',
location: {
start: {line: 6, column: 6},
end: {line: 6, column: 8},
source: 'db',
},
name: 'db',
},
value: {
type: 'StringLiteral',
location: {
start: {line: 6, column: 10},
end: {line: 6, column: 20},
source: '"telegraf"',
},
value: 'telegraf',
},
},
],
},
],
},
call: {
type: 'CallExpression',
location: {
start: {line: 7, column: 5},
end: {line: 7, column: 13},
source: 'filter()',
},
callee: {
type: 'Identifier',
location: {
start: {line: 7, column: 5},
end: {line: 7, column: 11},
source: 'filter',
},
name: 'filter',
},
},
},
call: {
type: 'CallExpression',
location: {
start: {line: 8, column: 5},
end: {line: 8, column: 23},
source: 'range(start: -15m)',
},
callee: {
type: 'Identifier',
location: {
start: {line: 8, column: 5},
end: {line: 8, column: 10},
source: 'range',
},
name: 'range',
},
arguments: [
{
type: 'ObjectExpression',
location: {
start: {line: 8, column: 11},
end: {line: 8, column: 22},
source: 'start: -15m',
},
properties: [
{
type: 'Property',
location: {
start: {line: 8, column: 11},
end: {line: 8, column: 22},
source: 'start: -15m',
},
key: {
type: 'Identifier',
location: {
start: {line: 8, column: 11},
end: {line: 8, column: 16},
source: 'start',
},
name: 'start',
},
value: {
type: 'UnaryExpression',
location: {
start: {line: 8, column: 18},
end: {line: 8, column: 22},
source: '-15m',
},
operator: '-',
argument: {
type: 'DurationLiteral',
location: {
start: {line: 8, column: 19},
end: {line: 8, column: 22},
source: '15m',
},
value: '15m0s',
},
},
},
],
},
],
},
},
},
],
}

View File

@ -7,16 +7,17 @@ describe('IFQL.AST.Walker', () => {
describe('Walker#functions', () => {
describe('simple example', () => {
describe('a single expression', () => {
it('returns a flattened ordered list of from and its arguments', () => {
it('returns a flattened ordered list of from and its args', () => {
const walker = new Walker(From)
expect(walker.stuff).toEqual([
expect(walker.body).toEqual([
{
type: 'CallExpression',
source: 'from(db: "telegraf")',
funcs: [
{
name: 'from',
source: 'from(db: "telegraf")',
arguments: [
args: [
{
key: 'db',
value: 'telegraf',
@ -32,7 +33,7 @@ describe('IFQL.AST.Walker', () => {
describe('a single string literal variable', () => {
it('returns the expected list', () => {
const walker = new Walker(StringLiteral)
expect(walker.stuff).toEqual([
expect(walker.body).toEqual([
{
type: 'VariableDeclaration',
source: 'bux = "im a var"',
@ -51,7 +52,7 @@ describe('IFQL.AST.Walker', () => {
describe('a single expression variable', () => {
it('returns the expected list', () => {
const walker = new Walker(Expression)
expect(walker.stuff).toEqual([
expect(walker.body).toEqual([
{
type: 'VariableDeclaration',
source: 'tele = from(db: "telegraf")',
@ -64,7 +65,7 @@ describe('IFQL.AST.Walker', () => {
{
name: 'from',
source: 'from(db: "telegraf")',
arguments: [
args: [
{
key: 'db',
value: 'telegraf',
@ -83,22 +84,23 @@ describe('IFQL.AST.Walker', () => {
})
describe('complex example', () => {
it('returns a flattened ordered list of all funcs and their arguments', () => {
it('returns a flattened ordered list of all funcs and their args', () => {
const walker = new Walker(Complex)
expect(walker.stuff).toEqual([
expect(walker.body).toEqual([
{
type: 'PipeExpression',
source:
'from(db: "telegraf") |> filter(fn: (r) => r["_measurement"] == "cpu") |> range(start: -1m)',
funcs: [
{
name: 'from',
source: 'from(db: "telegraf")',
arguments: [{key: 'db', value: 'telegraf'}],
args: [{key: 'db', value: 'telegraf'}],
},
{
name: 'filter',
source: '|> filter(fn: (r) => r["_measurement"] == "cpu")',
arguments: [
args: [
{
key: 'fn',
value: '(r) => r["_measurement"] == "cpu"',
@ -108,7 +110,7 @@ describe('IFQL.AST.Walker', () => {
{
name: 'range',
source: '|> range(start: -1m)',
arguments: [{key: 'start', value: '-1m'}],
args: [{key: 'start', value: '-1m'}],
},
],
},

View File

@ -0,0 +1,148 @@
import {bodyNodes} from 'src/ifql/helpers'
import suggestions from 'test/ifql/semantic_graph/suggestions'
import Variables from 'test/ifql/ast/variables'
import {Expression, StringLiteral} from 'test/ifql/ast/variable'
import From from 'test/ifql/ast/from'
const id = expect.any(String)
describe('IFQL.helpers', () => {
describe('bodyNodes', () => {
describe('bodyNodes for Expressions assigned to a variable', () => {
it('can parse an Expression assigned to a Variable', () => {
const actual = bodyNodes(Expression, suggestions)
const expected = [
{
declarations: [
{
funcs: [
{
args: [{key: 'db', type: 'string', value: 'telegraf'}],
id,
name: 'from',
source: 'from(db: "telegraf")',
},
],
id,
name: 'tele',
source: 'tele = from(db: "telegraf")',
type: 'CallExpression',
},
],
id,
source: 'tele = from(db: "telegraf")',
type: 'VariableDeclaration',
},
]
expect(actual).toEqual(expected)
})
})
describe('bodyNodes for a Literal assigned to a Variable', () => {
it('can parse an Expression assigned to a Variable', () => {
const actual = bodyNodes(StringLiteral, suggestions)
const expected = [
{
id,
source: 'bux = "im a var"',
type: 'VariableDeclaration',
declarations: [
{
id,
name: 'bux',
type: 'StringLiteral',
value: 'im a var',
},
],
},
]
expect(actual).toEqual(expected)
})
})
describe('bodyNodes for an Expression', () => {
it('can parse an Expression into bodyNodes', () => {
const actual = bodyNodes(From, suggestions)
const expected = [
{
declarations: [],
funcs: [
{
args: [{key: 'db', type: 'string', value: 'telegraf'}],
id,
name: 'from',
source: 'from(db: "telegraf")',
},
],
id,
source: 'from(db: "telegraf")',
type: 'CallExpression',
},
]
expect(actual).toEqual(expected)
})
})
})
describe('multiple bodyNodes', () => {
it('can parse variables and expressions together', () => {
const actual = bodyNodes(Variables, suggestions)
const expected = [
{
declarations: [
{
id,
name: 'bux',
type: 'StringLiteral',
value: 'ASDFASDFASDF',
},
],
id,
source: 'bux = "ASDFASDFASDF"',
type: 'VariableDeclaration',
},
{
declarations: [
{
funcs: [
{
args: [{key: 'db', type: 'string', value: 'foo'}],
id,
name: 'from',
source: 'from(db: "foo")',
},
],
id,
name: 'foo',
source: 'foo = from(db: "foo")',
type: 'CallExpression',
},
],
id,
source: 'foo = from(db: "foo")',
type: 'VariableDeclaration',
},
{
declarations: [],
funcs: [
{
args: [{key: 'db', type: 'string', value: 'bux'}],
id,
name: 'from',
source: 'from(db: bux)',
},
],
id,
source: 'from(db: bux)',
type: 'CallExpression',
},
]
expect(actual).toEqual(expected)
})
})
})

View File

@ -0,0 +1,93 @@
export default [
{
name: '_highestOrLowest',
params: {
_sortLimit: 'invalid',
by: 'invalid',
cols: 'array',
n: 'invalid',
reducer: 'function',
},
},
{
name: '_sortLimit',
params: {cols: 'array', desc: 'invalid', n: 'invalid'},
},
{name: 'bottom', params: {cols: 'array', n: 'invalid'}},
{name: 'count', params: {}},
{
name: 'cov',
params: {on: 'invalid', pearsonr: 'bool', x: 'invalid', y: 'invalid'},
},
{name: 'covariance', params: {pearsonr: 'bool'}},
{name: 'derivative', params: {nonNegative: 'bool', unit: 'duration'}},
{name: 'difference', params: {nonNegative: 'bool'}},
{name: 'distinct', params: {column: 'string'}},
{name: 'filter', params: {fn: 'function'}},
{name: 'first', params: {column: 'string', useRowTime: 'bool'}},
{name: 'from', params: {db: 'string'}},
{name: 'group', params: {by: 'array', except: 'array', keep: 'array'}},
{
name: 'highestAverage',
params: {by: 'invalid', cols: 'array', n: 'invalid'},
},
{
name: 'highestCurrent',
params: {by: 'invalid', cols: 'array', n: 'invalid'},
},
{name: 'highestMax', params: {by: 'invalid', cols: 'array', n: 'invalid'}},
{name: 'integral', params: {unit: 'duration'}},
{name: 'join', params: {}},
{name: 'last', params: {column: 'string', useRowTime: 'bool'}},
{name: 'limit', params: {}},
{
name: 'lowestAverage',
params: {by: 'invalid', cols: 'array', n: 'invalid'},
},
{
name: 'lowestCurrent',
params: {by: 'invalid', cols: 'array', n: 'invalid'},
},
{name: 'lowestMin', params: {by: 'invalid', cols: 'array', n: 'invalid'}},
{name: 'map', params: {fn: 'function'}},
{name: 'max', params: {column: 'string', useRowTime: 'bool'}},
{name: 'mean', params: {}},
{name: 'median', params: {compression: 'float', exact: 'bool'}},
{name: 'min', params: {column: 'string', useRowTime: 'bool'}},
{name: 'pearsonr', params: {on: 'invalid', x: 'invalid', y: 'invalid'}},
{name: 'percentile', params: {p: 'float'}},
{name: 'range', params: {start: 'time', stop: 'time'}},
{name: 'sample', params: {column: 'string', useRowTime: 'bool'}},
{name: 'set', params: {key: 'string', value: 'string'}},
{name: 'shift', params: {shift: 'duration'}},
{name: 'skew', params: {}},
{name: 'sort', params: {cols: 'array'}},
{name: 'spread', params: {}},
{name: 'stateCount', params: {fn: 'invalid', label: 'string'}},
{
name: 'stateDuration',
params: {fn: 'invalid', label: 'string', unit: 'duration'},
},
{
name: 'stateTracking',
params: {
countLabel: 'string',
durationLabel: 'string',
durationUnit: 'duration',
fn: 'function',
},
},
{name: 'stddev', params: {}},
{name: 'sum', params: {}},
{name: 'top', params: {cols: 'array', n: 'invalid'}},
{
name: 'window',
params: {
every: 'duration',
period: 'duration',
round: 'duration',
start: 'time',
},
},
{name: 'yield', params: {name: 'string'}},
]