Populate Filter funciton from ast
parent
f1c27da71e
commit
4988f0932b
|
@ -1,6 +1,11 @@
|
||||||
// Texas Ranger
|
// Texas Ranger
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import {FlatBody, Func} from 'src/types/ifql'
|
import {
|
||||||
|
Func,
|
||||||
|
FlatBody,
|
||||||
|
BinaryExpressionNode,
|
||||||
|
MemberExpressionNode,
|
||||||
|
} from 'src/types/ifql'
|
||||||
|
|
||||||
interface Expression {
|
interface Expression {
|
||||||
argument: object
|
argument: object
|
||||||
|
@ -29,6 +34,8 @@ interface AST {
|
||||||
body: Body[]
|
body: Body[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InOrderNode = BinaryExpressionNode | MemberExpressionNode
|
||||||
|
|
||||||
export default class Walker {
|
export default class Walker {
|
||||||
private ast: AST
|
private ast: AST
|
||||||
|
|
||||||
|
@ -51,6 +58,51 @@ export default class Walker {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get inOrderExpression(): InOrderNode[] {
|
||||||
|
const tree = _.get(this.ast, 'body.0.expression.body', new Array<Body>())
|
||||||
|
return this.inOrder(tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inOrder = (node): InOrderNode[] => {
|
||||||
|
let results = []
|
||||||
|
if (node) {
|
||||||
|
results = [...results, ...this.inOrder(node.left)]
|
||||||
|
|
||||||
|
if (node.type === 'MemberExpression') {
|
||||||
|
const {location, object, property} = node
|
||||||
|
const {name} = object
|
||||||
|
const {value, type} = property
|
||||||
|
const {source} = location
|
||||||
|
|
||||||
|
results = [
|
||||||
|
...results,
|
||||||
|
{
|
||||||
|
source,
|
||||||
|
object: {name, type: object.type},
|
||||||
|
property: {value, type},
|
||||||
|
type: node.type,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.operator) {
|
||||||
|
results = [...results, {type: 'Operator', source: node.operator}]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.name) {
|
||||||
|
results = [...results, {type: node.type, source: node.location.source}]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.value) {
|
||||||
|
results = [...results, {type: node.type, source: node.location.source}]
|
||||||
|
}
|
||||||
|
|
||||||
|
results = [...results, ...this.inOrder(node.right)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
private variable(variable) {
|
private variable(variable) {
|
||||||
const {location} = variable
|
const {location} = variable
|
||||||
const declarations = variable.declarations.map(d => {
|
const declarations = variable.declarations.map(d => {
|
||||||
|
@ -63,7 +115,7 @@ export default class Walker {
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
params: this.params(init.params),
|
params: this.params(init.params),
|
||||||
body: this.binaryExpressionInOrder(init.body),
|
body: this.inOrder(init.body),
|
||||||
source: init.location.source,
|
source: init.location.source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,46 +138,6 @@ export default class Walker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns an in order flattening of a binary expression
|
// returns an in order flattening of a binary expression
|
||||||
private inOrder = (node, result) => {
|
|
||||||
if (node) {
|
|
||||||
this.inOrder(node.left, result)
|
|
||||||
|
|
||||||
if (node.type === 'MemberExpression') {
|
|
||||||
const {location, object, property} = node
|
|
||||||
const {name} = object
|
|
||||||
const {value, type} = property
|
|
||||||
const {source} = location.source
|
|
||||||
|
|
||||||
result.push({
|
|
||||||
source,
|
|
||||||
object: {name, type: object.type},
|
|
||||||
property: {value, type},
|
|
||||||
type: node.type,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.operator) {
|
|
||||||
result.push({type: 'Operator', source: node.operator})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.name) {
|
|
||||||
result.push({type: node.type, source: node.location.source})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value) {
|
|
||||||
result.push({type: node.type, source: node.location.source})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.inOrder(node.right, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private binaryExpressionInOrder = tree => {
|
|
||||||
const result = []
|
|
||||||
this.inOrder(tree, result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private expression(expression, location): FlatExpression {
|
private expression(expression, location): FlatExpression {
|
||||||
const funcs = this.buildFuncNodes(this.walk(expression))
|
const funcs = this.buildFuncNodes(this.walk(expression))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React, {PureComponent} from 'react'
|
||||||
|
import {connect} from 'react-redux'
|
||||||
|
import {getAST} from 'src/ifql/apis'
|
||||||
|
import {
|
||||||
|
Links,
|
||||||
|
OnChangeArg,
|
||||||
|
BinaryExpressionNode,
|
||||||
|
MemberExpressionNode,
|
||||||
|
} from 'src/types/ifql'
|
||||||
|
import Walker from 'src/ifql/ast/walker'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
argKey: string
|
||||||
|
funcID: string
|
||||||
|
bodyID: string
|
||||||
|
declarationID: string
|
||||||
|
value: string
|
||||||
|
onChangeArg: OnChangeArg
|
||||||
|
links: Links
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilterNode = BinaryExpressionNode | MemberExpressionNode
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
nodes: FilterNode[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Filter extends PureComponent<Props, State> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
nodes: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
const {links, value} = this.props
|
||||||
|
try {
|
||||||
|
const ast = await getAST({url: links.ast, body: value})
|
||||||
|
const nodes = new Walker(ast).inOrderExpression
|
||||||
|
this.setState({nodes})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Could not parse AST', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {argKey} = this.props
|
||||||
|
const {nodes} = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="func-arg">
|
||||||
|
<label className="func-arg--label">{argKey}</label>
|
||||||
|
{nodes.map((n, i) => {
|
||||||
|
return <div key={i}>{n.source}</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = ({links}) => {
|
||||||
|
return {links: links.ifql}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(Filter)
|
|
@ -4,6 +4,7 @@ import FuncArgInput from 'src/ifql/components/FuncArgInput'
|
||||||
import FuncArgBool from 'src/ifql/components/FuncArgBool'
|
import FuncArgBool from 'src/ifql/components/FuncArgBool'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
import From from 'src/ifql/components/From'
|
import From from 'src/ifql/components/From'
|
||||||
|
import Filter from 'src/ifql/components/Filter'
|
||||||
|
|
||||||
import {funcNames, argTypes} from 'src/ifql/constants'
|
import {funcNames, argTypes} from 'src/ifql/constants'
|
||||||
import {OnChangeArg} from 'src/types/ifql'
|
import {OnChangeArg} from 'src/types/ifql'
|
||||||
|
@ -48,6 +49,19 @@ class FuncArg extends PureComponent<Props> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (funcName === funcNames.FILTER) {
|
||||||
|
return (
|
||||||
|
<Filter
|
||||||
|
argKey={argKey}
|
||||||
|
funcID={funcID}
|
||||||
|
bodyID={bodyID}
|
||||||
|
value={this.value}
|
||||||
|
declarationID={declarationID}
|
||||||
|
onChangeArg={onChangeArg}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case argTypes.STRING:
|
case argTypes.STRING:
|
||||||
case argTypes.DURATION:
|
case argTypes.DURATION:
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
export const FROM = 'from'
|
export const FROM = 'from'
|
||||||
|
export const FILTER = 'filter'
|
||||||
|
|
|
@ -5,7 +5,7 @@ import _ from 'lodash'
|
||||||
|
|
||||||
import TimeMachine from 'src/ifql/components/TimeMachine'
|
import TimeMachine from 'src/ifql/components/TimeMachine'
|
||||||
import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
|
import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
|
||||||
import {Suggestion, FlatBody} from 'src/types/ifql'
|
import {Suggestion, FlatBody, Links} from 'src/types/ifql'
|
||||||
import {InputArg, Handlers, DeleteFuncNodeArgs, Func} from 'src/types/ifql'
|
import {InputArg, Handlers, DeleteFuncNodeArgs, Func} from 'src/types/ifql'
|
||||||
|
|
||||||
import {bodyNodes} from 'src/ifql/helpers'
|
import {bodyNodes} from 'src/ifql/helpers'
|
||||||
|
@ -13,12 +13,6 @@ import {getSuggestions, getAST} from 'src/ifql/apis'
|
||||||
import * as argTypes from 'src/ifql/constants/argumentTypes'
|
import * as argTypes from 'src/ifql/constants/argumentTypes'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
interface Links {
|
|
||||||
self: string
|
|
||||||
suggestions: string
|
|
||||||
ast: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
links: Links
|
links: Links
|
||||||
}
|
}
|
||||||
|
@ -44,7 +38,10 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
body: [],
|
body: [],
|
||||||
ast: null,
|
ast: null,
|
||||||
suggestions: [],
|
suggestions: [],
|
||||||
script: 'addOne = (n) => n + 1\n\n',
|
script: `from(db:"foo")
|
||||||
|
|> filter(fn: (r) => r["_measurement"]=="cpu" AND
|
||||||
|
r["_field"] == "usage_system" AND
|
||||||
|
r["service"] == "app-server")`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,29 @@ export interface InputArg {
|
||||||
value: string | boolean
|
value: string | boolean
|
||||||
generate?: boolean
|
generate?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flattened AST
|
// Flattened AST
|
||||||
|
export interface BinaryExpressionNode {
|
||||||
|
source: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ObjectNode {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PropertyNode {
|
||||||
|
value: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MemberExpressionNode {
|
||||||
|
source: string
|
||||||
|
object: ObjectNode
|
||||||
|
property: PropertyNode
|
||||||
|
}
|
||||||
|
|
||||||
export interface FlatBody {
|
export interface FlatBody {
|
||||||
type: string
|
type: string
|
||||||
source: string
|
source: string
|
||||||
|
@ -75,3 +97,9 @@ export interface Suggestion {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Links {
|
||||||
|
self: string
|
||||||
|
suggestions: string
|
||||||
|
ast: string
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {shallow} from 'enzyme'
|
||||||
|
import {Filter} from 'src/ifql/components/Filter'
|
||||||
|
|
||||||
|
const setup = (override = {}) => {
|
||||||
|
const props = {
|
||||||
|
argKey: 'fn',
|
||||||
|
funcID: 'f1',
|
||||||
|
bodyID: 'b1',
|
||||||
|
declarationID: 'd1',
|
||||||
|
value: '(r) => r["measurement"] === "m1"',
|
||||||
|
onChangeArg: () => {},
|
||||||
|
links: {
|
||||||
|
self: '',
|
||||||
|
ast: '',
|
||||||
|
suggestions: '',
|
||||||
|
},
|
||||||
|
...override,
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = shallow(<Filter {...props} />)
|
||||||
|
|
||||||
|
return {
|
||||||
|
wrapper,
|
||||||
|
props,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('IFQL.Components.Filter', () => {
|
||||||
|
describe('rendering', () => {
|
||||||
|
it('renders without errors', () => {
|
||||||
|
const {wrapper} = setup()
|
||||||
|
expect(wrapper.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue