Populate Filter funciton from ast
parent
7bc346a22e
commit
acc84cccc4
|
@ -1,6 +1,11 @@
|
|||
// Texas Ranger
|
||||
import _ from 'lodash'
|
||||
import {FlatBody, Func} from 'src/types/ifql'
|
||||
import {
|
||||
Func,
|
||||
FlatBody,
|
||||
BinaryExpressionNode,
|
||||
MemberExpressionNode,
|
||||
} from 'src/types/ifql'
|
||||
|
||||
interface Expression {
|
||||
argument: object
|
||||
|
@ -29,6 +34,8 @@ interface AST {
|
|||
body: Body[]
|
||||
}
|
||||
|
||||
type InOrderNode = BinaryExpressionNode | MemberExpressionNode
|
||||
|
||||
export default class Walker {
|
||||
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) {
|
||||
const {location} = variable
|
||||
const declarations = variable.declarations.map(d => {
|
||||
|
@ -63,7 +115,7 @@ export default class Walker {
|
|||
name,
|
||||
type,
|
||||
params: this.params(init.params),
|
||||
body: this.binaryExpressionInOrder(init.body),
|
||||
body: this.inOrder(init.body),
|
||||
source: init.location.source,
|
||||
}
|
||||
}
|
||||
|
@ -86,46 +138,6 @@ export default class Walker {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
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 {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import From from 'src/ifql/components/From'
|
||||
import Filter from 'src/ifql/components/Filter'
|
||||
|
||||
import {funcNames, argTypes} from 'src/ifql/constants'
|
||||
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) {
|
||||
case argTypes.STRING:
|
||||
case argTypes.DURATION:
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
export const FROM = 'from'
|
||||
export const FILTER = 'filter'
|
||||
|
|
|
@ -5,7 +5,7 @@ import _ from 'lodash'
|
|||
|
||||
import TimeMachine from 'src/ifql/components/TimeMachine'
|
||||
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 {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 {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Links {
|
||||
self: string
|
||||
suggestions: string
|
||||
ast: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
links: Links
|
||||
}
|
||||
|
@ -44,7 +38,10 @@ export class IFQLPage extends PureComponent<Props, State> {
|
|||
body: [],
|
||||
ast: null,
|
||||
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
|
||||
generate?: boolean
|
||||
}
|
||||
|
||||
// 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 {
|
||||
type: string
|
||||
source: string
|
||||
|
@ -75,3 +97,9 @@ export interface Suggestion {
|
|||
[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