Merge pull request #3536 from influxdata/ifql/join

Ifql/join
pull/10616/head
Deniz Kusefoglu 2018-06-04 10:13:01 -07:00 committed by GitHub
commit f493ee1cec
18 changed files with 857 additions and 66 deletions

View File

@ -238,15 +238,42 @@ export default class Walker {
})
}
private constructObject(value) {
const propArray = _.get(value, 'properties', [])
const valueObj = propArray.reduce((acc, p) => {
return {...acc, [p.key.name]: p.value.name}
}, {})
return valueObj
}
private constructArray(value) {
const elementsArray = _.get(value, 'elements', [])
const valueArray = elementsArray.reduce((acc, e) => {
return [...acc, e.value]
}, [])
return valueArray
}
private getProperties = props => {
return props.map(prop => ({
key: prop.key.name,
value: _.get(
prop,
'value.value',
_.get(prop, 'value.location.source', '')
),
}))
return props.map(prop => {
const key = prop.key.name
let value
if (_.get(prop, 'value.type', '') === 'ObjectExpression') {
value = this.constructObject(prop.value)
} else if (_.get(prop, 'value.type', '') === 'ArrayExpression') {
value = this.constructArray(prop.value)
} else {
value = _.get(
prop,
'value.value',
_.get(prop, 'value.location.source', '')
)
}
return {
key,
value,
}
})
}
private get baseExpression() {

View File

@ -35,6 +35,7 @@ class BodyBuilder extends PureComponent<Props> {
declarationID={d.id}
funcNames={this.funcNames}
funcs={d.funcs}
declarationsFromBody={this.declarationsFromBody}
/>
</div>
)
@ -54,6 +55,7 @@ class BodyBuilder extends PureComponent<Props> {
bodyID={b.id}
funcs={b.funcs}
funcNames={this.funcNames}
declarationsFromBody={this.declarationsFromBody}
/>
</div>
)
@ -84,6 +86,20 @@ class BodyBuilder extends PureComponent<Props> {
return declarationFunctions
}
private get declarationsFromBody(): string[] {
const {body} = this.props
const declarations = _.flatten(
body.map(b => {
if ('declarations' in b) {
const declarationsArray = b.declarations
return declarationsArray.map(da => da.name)
}
return []
})
)
return declarations
}
private createNewBody = name => {
if (name === funcNames.FROM) {
this.props.onAppendFrom()

View File

@ -11,12 +11,19 @@ interface Props {
bodyID: string
funcs: Func[]
declarationID?: string
declarationsFromBody: string[]
}
// an Expression is a group of one or more functions
class ExpressionNode extends PureComponent<Props> {
public render() {
const {declarationID, bodyID, funcNames, funcs} = this.props
const {
declarationID,
bodyID,
funcNames,
funcs,
declarationsFromBody,
} = this.props
return (
<IFQLContext.Consumer>
{({
@ -38,6 +45,7 @@ class ExpressionNode extends PureComponent<Props> {
onDelete={onDeleteFuncNode}
declarationID={declarationID}
onGenerateScript={onGenerateScript}
declarationsFromBody={declarationsFromBody}
/>
))}
<FuncSelector

View File

@ -15,7 +15,7 @@ interface Props {
funcName: string
funcID: string
argKey: string
value: string | boolean
value: string | boolean | {[x: string]: string}
type: string
bodyID: string
declarationID: string

View File

@ -19,10 +19,10 @@ interface State {
}
@ErrorHandling
class FuncArgInput extends PureComponent<Props, State> {
class FuncArgTextArea extends PureComponent<Props, State> {
private ref: React.RefObject<HTMLTextAreaElement>
constructor(props) {
constructor(props: Props) {
super(props)
this.ref = React.createRef()
this.state = {
@ -99,4 +99,4 @@ class FuncArgInput extends PureComponent<Props, State> {
}
}
export default FuncArgInput
export default FuncArgTextArea

View File

@ -4,6 +4,7 @@ import {OnChangeArg} from 'src/types/ifql'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Func} from 'src/types/ifql'
import {funcNames} from 'src/ifql/constants'
import Join from 'src/ifql/components/Join'
import {Service} from 'src/types'
interface Props {
@ -14,6 +15,7 @@ interface Props {
declarationID: string
onGenerateScript: () => void
onDeleteFunc: () => void
declarationsFromBody: string[]
}
@ErrorHandling
@ -27,27 +29,37 @@ export default class FuncArgs extends PureComponent<Props> {
onDeleteFunc,
declarationID,
onGenerateScript,
declarationsFromBody,
} = this.props
const {name: funcName, id: funcID} = func
return (
<div className="func-node--tooltip">
{func.args.map(({key, value, type}) => {
return (
{funcName === funcNames.JOIN ? (
<Join
func={func}
bodyID={bodyID}
declarationID={declarationID}
onChangeArg={onChangeArg}
declarationsFromBody={declarationsFromBody}
onGenerateScript={onGenerateScript}
/>
) : (
func.args.map(({key, value, type}) => (
<FuncArg
key={key}
type={type}
argKey={key}
value={value}
bodyID={bodyID}
funcID={func.id}
funcID={funcID}
funcName={funcName}
service={service}
funcName={func.name}
onChangeArg={onChangeArg}
declarationID={declarationID}
onGenerateScript={onGenerateScript}
/>
)
})}
))
)}
<div className="func-node--buttons">
<div
className="btn btn-sm btn-danger func-node--delete"

View File

@ -1,4 +1,5 @@
import React, {PureComponent} from 'react'
import uuid from 'uuid'
import _ from 'lodash'
import {Func} from 'src/types/ifql'
@ -6,7 +7,7 @@ import {funcNames} from 'src/ifql/constants'
import Filter from 'src/ifql/components/Filter'
import FilterPreview from 'src/ifql/components/FilterPreview'
import uuid from 'uuid'
import {getDeep} from 'src/utils/wrappers'
interface Props {
func: Func
@ -26,7 +27,7 @@ export default class FuncArgsPreview extends PureComponent<Props> {
}
if (func.name === funcNames.FILTER) {
const value = _.get(args, '0.value', '')
const value = getDeep<string>(args, '0.value', '')
if (!value) {
return this.colorizedArguments
}
@ -51,11 +52,18 @@ export default class FuncArgsPreview extends PureComponent<Props> {
}
const separator = i === 0 ? null : ', '
let argValue
if (arg.type === 'object') {
const valueMap = _.map(arg.value, (value, key) => `${key}:${value}`)
argValue = `{${valueMap.join(', ')}}`
} else {
argValue = `${arg.value}`
}
return (
<React.Fragment key={uuid.v4()}>
{separator}
{arg.key}: {this.colorArgType(`${arg.value}`, arg.type)}
{arg.key}: {this.colorArgType(argValue, arg.type)}
</React.Fragment>
)
})
@ -76,6 +84,9 @@ export default class FuncArgsPreview extends PureComponent<Props> {
case 'string': {
return <span className="variable-value--string">"{argument}"</span>
}
case 'object': {
return <span className="variable-value--object">{argument}</span>
}
case 'invalid': {
return <span className="variable-value--invalid">{argument}</span>
}

View File

@ -14,6 +14,7 @@ interface Props {
onDelete: OnDeleteFuncNode
onChangeArg: OnChangeArg
onGenerateScript: () => void
declarationsFromBody: string[]
}
interface State {
@ -41,6 +42,7 @@ export default class FuncNode extends PureComponent<Props, State> {
onChangeArg,
declarationID,
onGenerateScript,
declarationsFromBody,
} = this.props
const {isExpanded} = this.state
@ -61,6 +63,7 @@ export default class FuncNode extends PureComponent<Props, State> {
declarationID={declarationID}
onGenerateScript={onGenerateScript}
onDeleteFunc={this.handleDelete}
declarationsFromBody={declarationsFromBody}
/>
)}
</div>

View File

@ -0,0 +1,154 @@
import React, {PureComponent} from 'react'
import _ from 'lodash'
import Dropdown from 'src/shared/components/Dropdown'
import FuncArgInput from 'src/ifql/components/FuncArgInput'
import FuncArgTextArea from 'src/ifql/components/FuncArgTextArea'
import {getDeep} from 'src/utils/wrappers'
import {OnChangeArg, Func, Arg} from 'src/types/ifql'
import {argTypes} from 'src/ifql/constants'
interface Props {
func: Func
bodyID: string
declarationID: string
onChangeArg: OnChangeArg
declarationsFromBody: string[]
onGenerateScript: () => void
}
interface DropdownItem {
text: string
}
class Join extends PureComponent<Props> {
constructor(props: Props) {
super(props)
}
public render() {
const {
func,
bodyID,
onChangeArg,
declarationID,
onGenerateScript,
} = this.props
return (
<>
<div className="func-arg">
<label className="func-arg--label">tables</label>
<Dropdown
selected={this.table1Value}
className="from--dropdown dropdown-100 func-arg--value"
menuClass="dropdown-astronaut"
buttonColor="btn-default"
items={this.items}
onChoose={this.handleChooseTable1}
/>
<Dropdown
selected={this.table2Value}
className="from--dropdown dropdown-100 func-arg--value"
menuClass="dropdown-astronaut"
buttonColor="btn-default"
items={this.items}
onChoose={this.handleChooseTable2}
/>
</div>
<div className="func-arg">
<FuncArgInput
value={this.onValue}
argKey={'on'}
bodyID={bodyID}
funcID={func.id}
type={argTypes.STRING}
onChangeArg={onChangeArg}
declarationID={declarationID}
onGenerateScript={onGenerateScript}
/>
</div>
<div className="func-arg">
<FuncArgTextArea
type={argTypes.FUNCTION}
value={this.fnValue}
argKey={'fn'}
funcID={func.id}
bodyID={bodyID}
onChangeArg={onChangeArg}
declarationID={declarationID}
onGenerateScript={onGenerateScript}
/>
</div>
</>
)
}
private handleChooseTable1 = (item: DropdownItem): void => {
this.handleChooseTables(item.text, this.table2Value)
}
private handleChooseTable2 = (item: DropdownItem): void => {
this.handleChooseTables(this.table1Value, item.text)
}
private handleChooseTables = (table1: string, table2: string): void => {
const {
onChangeArg,
bodyID,
declarationID,
func,
onGenerateScript,
} = this.props
onChangeArg({
funcID: func.id,
bodyID,
declarationID,
key: 'tables',
value: {[table1]: table1, [table2]: table2},
generate: true,
})
onGenerateScript()
}
private get items(): DropdownItem[] {
return this.props.declarationsFromBody.map(d => ({text: d}))
}
private get argsArray(): Arg[] {
const {func} = this.props
return getDeep<Arg[]>(func, 'args', [])
}
private get onValue(): string {
const onObject = this.argsArray.find(a => a.key === 'on')
return onObject.value.toString()
}
private get fnValue(): string {
const fnObject = this.argsArray.find(a => a.key === 'fn')
return fnObject.value.toString()
}
private get table1Value(): string {
const tables = this.argsArray.find(a => a.key === 'tables')
if (tables) {
const keys = _.keys(tables.value)
return getDeep<string>(keys, '0', '')
}
return ''
}
private get table2Value(): string {
const tables = this.argsArray.find(a => a.key === 'tables')
if (tables) {
const keys = _.keys(tables.value)
return getDeep<string>(keys, '1', getDeep<string>(keys, '0', ''))
}
return ''
}
}
export default Join

View File

@ -1,2 +1,2 @@
export const NEW_FROM = `from(db: "pick a db")\n\t|> filter(fn: (r) => r.tag == "value")\n\t|> range(start: -1m)`
export const NEW_JOIN = `join(tables: {fil:fil, tele:tele}, on:["host"], fn: (tables) => tables.fil["_value"] + tables.tele["_value"])`
export const NEW_JOIN = `join(tables:{fil:fil, tele:tele}, on:["host"], fn:(tables) => tables.fil["_value"] + tables.tele["_value"])`

View File

@ -14,7 +14,7 @@ import {UpdateScript} from 'src/ifql/actions'
import {bodyNodes} from 'src/ifql/helpers'
import {getSuggestions, getAST, getTimeSeries} from 'src/ifql/apis'
import {funcNames, builder, argTypes} from 'src/ifql/constants'
import {builder, argTypes} from 'src/ifql/constants'
import {Source, Service, Notification, ScriptResult} from 'src/types'
import {
@ -225,7 +225,7 @@ export class IFQLPage extends PureComponent<Props, State> {
}
if (!declaration.funcs) {
return `${acc}${b.source}\n\n`
return `${acc}${b.source}`
}
return `${acc}${declaration.name} = ${this.funcsToScript(
@ -253,7 +253,12 @@ export class IFQLPage extends PureComponent<Props, State> {
}
if (type === argTypes.ARRAY) {
return `${key}: [${value}]`
return `${key}: ["${value}"]`
}
if (type === argTypes.OBJECT) {
const valueString = _.map(value, (v, k) => k + ':' + v).join(',')
return `${key}: {${valueString}}`
}
return `${key}: ${value}`
@ -402,20 +407,7 @@ export class IFQLPage extends PureComponent<Props, State> {
try {
const ast = await getAST({url: links.ast, body: script})
const suggestions = this.state.suggestions.map(s => {
if (s.name === funcNames.JOIN) {
return {
...s,
params: {
tables: 'object',
on: 'array',
fn: 'function',
},
}
}
return s
})
const body = bodyNodes(ast, suggestions)
const body = bodyNodes(ast, this.state.suggestions)
const status = {type: 'success', text: ''}
this.setState({ast, body, status})
this.props.updateScript(script)

View File

@ -1,13 +1,15 @@
import uuid from 'uuid'
import _ from 'lodash'
import Walker from 'src/ifql/ast/walker'
import {FlatBody, Func} from 'src/types/ifql'
import {FlatBody, Func, Suggestion} from 'src/types/ifql'
interface Body extends FlatBody {
id: string
}
export const bodyNodes = (ast, suggestions): Body[] => {
export const bodyNodes = (ast, suggestions: Suggestion[]): Body[] => {
if (!ast) {
return []
}
@ -47,11 +49,12 @@ export const bodyNodes = (ast, suggestions): Body[] => {
return body
}
const functions = (funcs, suggestions): Func[] => {
const functions = (funcs: Func[], suggestions: Suggestion[]): Func[] => {
const funcList = funcs.map(func => {
const suggestion = suggestions.find(f => f.name === func.name)
if (!suggestion) {
return {
type: func.type,
id: uuid.v4(),
source: func.source,
name: func.name,
@ -61,7 +64,8 @@ const functions = (funcs, suggestions): Func[] => {
const {params, name} = suggestion
const args = Object.entries(params).map(([key, type]) => {
const value = _.get(func.args.find(arg => arg.key === key), 'value', '')
const argWithKey = func.args.find(arg => arg.key === key)
const value = _.get(argWithKey, 'value', '')
return {
key,
@ -71,6 +75,7 @@ const functions = (funcs, suggestions): Func[] => {
})
return {
type: func.type,
id: uuid.v4(),
source: func.source,
name,

View File

@ -59,6 +59,10 @@ export const modeIFQL = {
regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
token: 'number',
},
{
regex: /({.+:.+})/,
token: 'object',
},
{
regex: /\/\/.*/,
token: 'comment',

View File

@ -34,6 +34,9 @@
.cm-boolean {
color: $c-viridian;
}
.cm-object {
color: $c-honeydew;
}
.cm-null {
color: $c-dreamsicle;
}

View File

@ -4,6 +4,7 @@ $ifql-node-gap: 5px;
$ifql-node-padding: 10px;
$ifql-arg-min-width: 120px;
$ifql-number-color: $c-neutrino;
$ifql-object-color: $c-viridian;
$ifql-string-color: $c-honeydew;
$ifql-boolean-color: $c-viridian;
$ifql-invalid-color: $c-viridian;
@ -76,6 +77,10 @@ $ifql-invalid-color: $c-viridian;
color: $ifql-number-color;
}
.variable-value--object {
color: $ifql-object-color;
}
.variable-value--invalid {
color: $ifql-invalid-color;
}
@ -257,16 +262,16 @@ $ifql-filter-parens: $g5-pepper;
padding: 0 ($ifql-filter-gap / 2);
}
.ifql-filter--value+.ifql-filter--operator,
.ifql-filter--paren-close+.ifql-filter--operator {
.ifql-filter--value + .ifql-filter--operator,
.ifql-filter--paren-close + .ifql-filter--operator {
padding: 0 $ifql-filter-gap;
}
.ifql-filter--key+.ifql-filter--operator {
.ifql-filter--key + .ifql-filter--operator {
background-color: $ifql-filter-expression;
}
.ifql-filter--key+.ifql-filter--operator+.ifql-filter--value {
.ifql-filter--key + .ifql-filter--operator + .ifql-filter--value {
background-color: $ifql-filter-expression;
border-radius: 0 3px 3px 0;
}
@ -291,7 +296,8 @@ $ifql-filter-parens: $g5-pepper;
height: $ifql-filter-unit-wrapped;
width: ($ifql-filter-unit-wrapped - $ifql-filter-unit) / 2;
background-color: $ifql-filter-parens;
border: (($ifql-filter-unit-wrapped - $ifql-filter-unit) / 2) solid $ifql-filter-expression;
border: (($ifql-filter-unit-wrapped - $ifql-filter-unit) / 2) solid
$ifql-filter-expression;
}
.ifql-filter--paren-open {

View File

@ -37,7 +37,7 @@ export interface InputArg {
bodyID: string
declarationID?: string
key: string
value: string | boolean
value: string | boolean | {[x: string]: string}
generate?: boolean
}
@ -80,11 +80,11 @@ export interface Func {
id: string
}
type Value = string | boolean
export type Arg = ArgString
export interface Arg {
export interface ArgString {
key: string
value: Value
value: string
type: string
}

View File

@ -345,3 +345,516 @@ export const Fork = {
},
],
}
export const JoinWithObjectArg = {
type: 'Program',
location: {
start: {
line: 1,
column: 1,
},
end: {
line: 1,
column: 106,
},
source:
'join(tables:{cpu:cpu, mem:mem}, on:["host"], fn: (tables) => tables.cpu["_value"] + tables.mem["_value"])',
},
body: [
{
type: 'ExpressionStatement',
location: {
start: {
line: 1,
column: 1,
},
end: {
line: 1,
column: 106,
},
source:
'join(tables:{cpu:cpu, mem:mem}, on:["host"], fn: (tables) => tables.cpu["_value"] + tables.mem["_value"])',
},
expression: {
type: 'CallExpression',
location: {
start: {
line: 1,
column: 1,
},
end: {
line: 1,
column: 106,
},
source:
'join(tables:{cpu:cpu, mem:mem}, on:["host"], fn: (tables) => tables.cpu["_value"] + tables.mem["_value"])',
},
callee: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 1,
},
end: {
line: 1,
column: 5,
},
source: 'join',
},
name: 'join',
},
arguments: [
{
type: 'ObjectExpression',
location: {
start: {
line: 1,
column: 6,
},
end: {
line: 1,
column: 105,
},
source:
'tables:{cpu:cpu, mem:mem}, on:["host"], fn: (tables) => tables.cpu["_value"] + tables.mem["_value"]',
},
properties: [
{
type: 'Property',
location: {
start: {
line: 1,
column: 6,
},
end: {
line: 1,
column: 31,
},
source: 'tables:{cpu:cpu, mem:mem}',
},
key: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 6,
},
end: {
line: 1,
column: 12,
},
source: 'tables',
},
name: 'tables',
},
value: {
type: 'ObjectExpression',
location: {
start: {
line: 1,
column: 14,
},
end: {
line: 1,
column: 30,
},
source: 'cpu:cpu, mem:mem',
},
properties: [
{
type: 'Property',
location: {
start: {
line: 1,
column: 14,
},
end: {
line: 1,
column: 21,
},
source: 'cpu:cpu',
},
key: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 14,
},
end: {
line: 1,
column: 17,
},
source: 'cpu',
},
name: 'cpu',
},
value: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 18,
},
end: {
line: 1,
column: 21,
},
source: 'cpu',
},
name: 'cpu',
},
},
{
type: 'Property',
location: {
start: {
line: 1,
column: 23,
},
end: {
line: 1,
column: 30,
},
source: 'mem:mem',
},
key: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 23,
},
end: {
line: 1,
column: 26,
},
source: 'mem',
},
name: 'mem',
},
value: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 27,
},
end: {
line: 1,
column: 30,
},
source: 'mem',
},
name: 'mem',
},
},
],
},
},
{
type: 'Property',
location: {
start: {
line: 1,
column: 33,
},
end: {
line: 1,
column: 44,
},
source: 'on:["host"]',
},
key: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 33,
},
end: {
line: 1,
column: 35,
},
source: 'on',
},
name: 'on',
},
value: {
type: 'ArrayExpression',
location: {
start: {
line: 1,
column: 37,
},
end: {
line: 1,
column: 43,
},
source: '"host"',
},
elements: [
{
type: 'StringLiteral',
location: {
start: {
line: 1,
column: 37,
},
end: {
line: 1,
column: 43,
},
source: '"host"',
},
value: 'host',
},
],
},
},
{
type: 'Property',
location: {
start: {
line: 1,
column: 46,
},
end: {
line: 1,
column: 105,
},
source:
'fn: (tables) => tables.cpu["_value"] + tables.mem["_value"]',
},
key: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 46,
},
end: {
line: 1,
column: 48,
},
source: 'fn',
},
name: 'fn',
},
value: {
type: 'ArrowFunctionExpression',
location: {
start: {
line: 1,
column: 50,
},
end: {
line: 1,
column: 105,
},
source:
'(tables) => tables.cpu["_value"] + tables.mem["_value"]',
},
params: [
{
type: 'Property',
location: {
start: {
line: 1,
column: 51,
},
end: {
line: 1,
column: 57,
},
source: 'tables',
},
key: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 51,
},
end: {
line: 1,
column: 57,
},
source: 'tables',
},
name: 'tables',
},
value: null,
},
],
body: {
type: 'BinaryExpression',
location: {
start: {
line: 1,
column: 62,
},
end: {
line: 1,
column: 105,
},
source: 'tables.cpu["_value"] + tables.mem["_value"]',
},
operator: '+',
left: {
type: 'MemberExpression',
location: {
start: {
line: 1,
column: 62,
},
end: {
line: 1,
column: 83,
},
source: 'tables.cpu["_value"] ',
},
object: {
type: 'MemberExpression',
location: {
start: {
line: 1,
column: 62,
},
end: {
line: 1,
column: 83,
},
source: 'tables.cpu["_value"] ',
},
object: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 62,
},
end: {
line: 1,
column: 68,
},
source: 'tables',
},
name: 'tables',
},
property: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 69,
},
end: {
line: 1,
column: 72,
},
source: 'cpu',
},
name: 'cpu',
},
},
property: {
type: 'StringLiteral',
location: {
start: {
line: 1,
column: 73,
},
end: {
line: 1,
column: 81,
},
source: '"_value"',
},
value: '_value',
},
},
right: {
type: 'MemberExpression',
location: {
start: {
line: 1,
column: 85,
},
end: {
line: 1,
column: 105,
},
source: 'tables.mem["_value"]',
},
object: {
type: 'MemberExpression',
location: {
start: {
line: 1,
column: 85,
},
end: {
line: 1,
column: 105,
},
source: 'tables.mem["_value"]',
},
object: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 85,
},
end: {
line: 1,
column: 91,
},
source: 'tables',
},
name: 'tables',
},
property: {
type: 'Identifier',
location: {
start: {
line: 1,
column: 92,
},
end: {
line: 1,
column: 95,
},
source: 'mem',
},
name: 'mem',
},
},
property: {
type: 'StringLiteral',
location: {
start: {
line: 1,
column: 96,
},
end: {
line: 1,
column: 104,
},
source: '"_value"',
},
value: '_value',
},
},
},
},
},
],
},
],
},
},
],
}

View File

@ -6,6 +6,7 @@ import {
Expression,
ArrowFunction,
Fork,
JoinWithObjectArg,
} from 'test/ifql/ast/variable'
describe('IFQL.AST.Walker', () => {
@ -14,7 +15,7 @@ describe('IFQL.AST.Walker', () => {
describe('a single expression', () => {
it('returns a flattened ordered list of from and its args', () => {
const walker = new Walker(From)
expect(walker.body).toEqual([
const expectedWalkerBody = [
{
type: 'CallExpression',
source: 'from(db: "telegraf")',
@ -31,14 +32,15 @@ describe('IFQL.AST.Walker', () => {
},
],
},
])
]
expect(walker.body).toEqual(expectedWalkerBody)
})
describe('variables', () => {
describe('a single string literal variable', () => {
it('returns the expected list', () => {
const walker = new Walker(StringLiteral)
expect(walker.body).toEqual([
const expectedWalkerBody = [
{
type: 'VariableDeclaration',
source: 'bux = "im a var"',
@ -50,14 +52,15 @@ describe('IFQL.AST.Walker', () => {
},
],
},
])
]
expect(walker.body).toEqual(expectedWalkerBody)
})
})
describe('a single expression variable', () => {
it('returns the expected list', () => {
const walker = new Walker(Expression)
expect(walker.body).toEqual([
const expectedWalkerBody = [
{
type: 'VariableDeclaration',
source: 'tele = from(db: "telegraf")',
@ -81,14 +84,15 @@ describe('IFQL.AST.Walker', () => {
},
],
},
])
]
expect(walker.body).toEqual(expectedWalkerBody)
})
})
describe('a single ArrowFunction variable', () => {
it('returns the expected list', () => {
const walker = new Walker(ArrowFunction)
expect(walker.body).toEqual([
const expectedWalkerBody = [
{
type: 'VariableDeclaration',
source: 'addOne = (n) => n + 1',
@ -106,14 +110,15 @@ describe('IFQL.AST.Walker', () => {
},
],
},
])
]
expect(walker.body).toEqual(expectedWalkerBody)
})
})
describe('forking', () => {
it('return the expected list of objects', () => {
const walker = new Walker(Fork)
expect(walker.body).toEqual([
const expectedWalkerBody = [
{
type: 'VariableDeclaration',
source: 'tele = from(db: "telegraf")',
@ -145,17 +150,48 @@ describe('IFQL.AST.Walker', () => {
{args: [], name: 'sum', source: '|> sum()'},
],
},
])
]
expect(walker.body).toEqual(expectedWalkerBody)
})
})
})
})
})
describe('Args that are objects', () => {
it('returns an object when arg type is object', () => {
const walker = new Walker(JoinWithObjectArg)
const expectedWalkerBody = [
{
type: 'CallExpression',
source:
'join(tables:{cpu:cpu, mem:mem}, on:["host"], fn: (tables) => tables.cpu["_value"] + tables.mem["_value"])',
funcs: [
{
name: 'join',
source:
'join(tables:{cpu:cpu, mem:mem}, on:["host"], fn: (tables) => tables.cpu["_value"] + tables.mem["_value"])',
args: [
{key: 'tables', value: {cpu: 'cpu', mem: 'mem'}},
{key: 'on', value: ['host']},
{
key: 'fn',
value:
'(tables) => tables.cpu["_value"] + tables.mem["_value"]',
},
],
},
],
},
]
expect(walker.body).toEqual(expectedWalkerBody)
})
})
describe('complex example', () => {
it('returns a flattened ordered list of all funcs and their args', () => {
const walker = new Walker(Complex)
expect(walker.body).toEqual([
const expectedWalkerBody = [
{
type: 'PipeExpression',
source:
@ -183,7 +219,8 @@ describe('IFQL.AST.Walker', () => {
},
],
},
])
]
expect(walker.body).toEqual(expectedWalkerBody)
})
})
})