Persist ifql script

Co-authored-by: Andrew Watkins <andrew.watkinz@gmail.com>
Co-authored-by: Chris Henn <chris.henn@influxdata.com>
ifql/save-service
Andrew Watkins 2018-05-22 14:50:07 -07:00
parent b778fd9756
commit 4326855844
11 changed files with 117 additions and 22 deletions

View File

@ -0,0 +1,17 @@
export type Action = ActionUpdateScript
export interface ActionUpdateScript {
type: 'UPDATE_SCRIPT'
payload: {
script: string
}
}
export type UpdateScript = (script: string) => ActionUpdateScript
export const updateScript = (script: string): ActionUpdateScript => {
return {
type: 'UPDATE_SCRIPT',
payload: {script},
}
}

View File

@ -1,4 +1,6 @@
import AJAX from 'src/utils/ajax'
import {Service} from 'src/types'
import {updateService} from 'src/shared/apis'
export const getSuggestions = async (url: string) => {
try {
@ -34,17 +36,24 @@ export const getAST = async (request: ASTRequest) => {
}
}
export const getTimeSeries = async (script: string) => {
export const getTimeSeries = async (service: Service, script: string) => {
const and = encodeURIComponent('&')
const mark = encodeURIComponent('?')
const garbage = script.replace(/\s/g, '') // server cannot handle whitespace
try {
const data = await AJAX({
method: 'POST',
url: `http://localhost:8093/query?q=${script}`,
url: `${
service.links.proxy
}?path=/v1/query${mark}orgName=defaulorgname${and}q=${garbage}`,
headers: {'Content-Type': 'text/plain'},
})
return data
} catch (error) {
console.error('Problem fetching data', error)
throw error
throw error.data.message
}
}
@ -82,3 +91,19 @@ export const getTagValues = async () => {
throw error
}
}
export const updateScript = async (service: Service, script: string) => {
const updates = {...service, metadata: {script}}
try {
const response = await updateService(updates)
return response
} catch (error) {
if (error.data) {
console.error('Could not update script', error.data)
throw error.data
}
throw error
}
}

View File

@ -58,3 +58,6 @@ export const EXCLUDED_KEYS = [
'Meta',
' ',
]
export const DEFAULT_SCRIPT =
'fil = (r) => r._measurement == "cpu"\ntele = from(db: "telegraf") \n\t\t|> filter(fn: fil)\n |> range(start: -1m)\n |> sum()\n\n'

View File

@ -10,11 +10,14 @@ import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
import {notify as notifyAction} from 'src/shared/actions/notifications'
import {analyzeSuccess} from 'src/shared/copy/notifications'
import {
updateScript as updateScriptAction,
UpdateScript,
} from 'src/ifql/actions'
import {bodyNodes} from 'src/ifql/helpers'
import {getSuggestions, getAST, getTimeSeries} from 'src/ifql/apis'
import {builder, argTypes} from 'src/ifql/constants'
import {funcNames} from 'src/ifql/constants'
import {funcNames, builder, argTypes} from 'src/ifql/constants'
import {Source, Service, Notification} from 'src/types'
import {
@ -37,6 +40,11 @@ interface Props {
services: Service[]
sources: Source[]
notify: (message: Notification) => void
script: string
updateScript: UpdateScript
params: {
sourceID: string
}
}
interface Body extends FlatBody {
@ -46,7 +54,6 @@ interface Body extends FlatBody {
interface State {
body: Body[]
ast: object
script: string
data: string
suggestions: Suggestion[]
status: Status
@ -63,7 +70,6 @@ export class IFQLPage extends PureComponent<Props, State> {
ast: null,
data: 'Hit "Get Data!" or Ctrl + Enter to run your script',
suggestions: [],
script: `fil = (r) => r._measurement == \"cpu\"\ntele = from(db: \"telegraf\") \n\t\t|> filter(fn: fil)\n |> range(start: -1m)\n |> sum()\n\n`,
status: {
type: 'none',
text: '',
@ -72,7 +78,7 @@ export class IFQLPage extends PureComponent<Props, State> {
}
public async componentDidMount() {
const {links} = this.props
const {links, script} = this.props
try {
const suggestions = await getSuggestions(links.suggestions)
@ -81,11 +87,12 @@ export class IFQLPage extends PureComponent<Props, State> {
console.error('Could not get function suggestions: ', error)
}
this.getASTResponse(this.state.script)
this.getASTResponse(script)
}
public render() {
const {suggestions, script, data, body, status} = this.state
const {suggestions, data, body, status} = this.state
const {script} = this.props
return (
<CheckServices>
@ -140,7 +147,7 @@ export class IFQLPage extends PureComponent<Props, State> {
}
private handleSubmitScript = () => {
this.getASTResponse(this.state.script)
this.getASTResponse(this.props.script)
}
private handleGenerateScript = (): void => {
@ -260,21 +267,21 @@ export class IFQLPage extends PureComponent<Props, State> {
}
private handleAppendFrom = (): void => {
const {script} = this.state
const {script} = this.props
const newScript = `${script.trim()}\n\n${builder.NEW_FROM}\n\n`
this.getASTResponse(newScript)
}
private handleAppendJoin = (): void => {
const {script} = this.state
const {script} = this.props
const newScript = `${script.trim()}\n\n${builder.NEW_JOIN}\n\n`
this.getASTResponse(newScript)
}
private handleChangeScript = (script: string): void => {
this.setState({script})
this.props.updateScript(script)
}
private handleAddNode = (
@ -376,10 +383,10 @@ export class IFQLPage extends PureComponent<Props, State> {
}
private handleAnalyze = async () => {
const {links, notify} = this.props
const {links, notify, script} = this.props
try {
const ast = await getAST({url: links.ast, body: this.state.script})
const ast = await getAST({url: links.ast, body: script})
const body = bodyNodes(ast, this.state.suggestions)
const status = {type: 'success', text: ''}
notify(analyzeSuccess)
@ -411,7 +418,8 @@ export class IFQLPage extends PureComponent<Props, State> {
})
const body = bodyNodes(ast, suggestions)
const status = {type: 'success', text: ''}
this.setState({ast, script, body, status})
this.setState({ast, body, status})
this.props.updateScript(script)
} catch (error) {
this.setState({status: this.parseError(error)})
return console.error('Could not parse AST', error)
@ -419,14 +427,14 @@ export class IFQLPage extends PureComponent<Props, State> {
}
private getTimeSeries = async () => {
const {script} = this.state
const {script} = this.props
this.setState({data: 'fetching data...'})
try {
const {data} = await getTimeSeries(script)
const {data} = await getTimeSeries(this.service, script)
this.setState({data})
} catch (error) {
this.setState({data: 'Error fetching data'})
this.setState({data: error})
console.error('Could not get timeSeries', error)
}
@ -440,12 +448,13 @@ export class IFQLPage extends PureComponent<Props, State> {
}
}
const mapStateToProps = ({links, services, sources}) => {
return {links: links.ifql, services, sources}
const mapStateToProps = ({links, services, sources, script}) => {
return {links: links.ifql, services, sources, script}
}
const mapDispatchToProps = {
notify: notifyAction,
updateScript: updateScriptAction,
}
export default connect(mapStateToProps, mapDispatchToProps)(IFQLPage)

View File

@ -0,0 +1,17 @@
import {Action} from 'src/ifql/actions'
import {editor} from 'src/ifql/constants'
const scriptReducer = (
state: string = editor.DEFAULT_SCRIPT,
action: Action
): string => {
switch (action.type) {
case 'UPDATE_SCRIPT': {
return action.payload.script
}
}
return state
}
export default scriptReducer

View File

@ -56,6 +56,7 @@ export const saveToLocalStorage = ({
timeRange,
dataExplorer,
dashTimeV1: {ranges},
script,
}: LocalStorage): void => {
try {
const appPersisted = {app: {persisted}}
@ -70,6 +71,7 @@ export const saveToLocalStorage = ({
dashTimeV1,
dataExplorer,
dataExplorerQueryConfigs,
script,
})
)
} catch (err) {

View File

@ -17,6 +17,7 @@ import overlayTechnology from 'src/shared/reducers/overlayTechnology'
import dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
import persistStateEnhancer from './persistStateEnhancer'
import servicesReducer from 'src/shared/reducers/services'
import scriptReducer from 'src/ifql/reducers/script'
const rootReducer = combineReducers({
...statusReducers,
@ -30,6 +31,7 @@ const rootReducer = combineReducers({
dashTimeV1,
routing: routerReducer,
services: servicesReducer,
script: scriptReducer,
})
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

View File

@ -7,6 +7,7 @@ export interface LocalStorage {
dataExplorer: DataExplorer
dataExplorerQueryConfigs: DataExplorerQueryConfigs
timeRange: TimeRange
script: string
}
export type VERSION = string

View File

@ -10,6 +10,7 @@ export interface NewService {
export interface Service {
id?: string
sourceID: string
url: string
name: string
type: string
@ -17,6 +18,9 @@ export interface Service {
password?: string
active: boolean
insecureSkipVerify: boolean
metadata: {
[x: string]: any
}
links: {
source: string
self: string

View File

@ -3,6 +3,7 @@ import {shallow} from 'enzyme'
import {IFQLPage} from 'src/ifql/containers/IFQLPage'
import TimeMachine from 'src/ifql/components/TimeMachine'
import {ActionUpdateScript} from 'src/ifql/actions'
jest.mock('src/ifql/apis', () => require('mocks/ifql/apis'))
@ -15,7 +16,19 @@ const setup = () => {
},
services: [],
sources: [],
script: '',
notify: () => {},
params: {
sourceID: '',
},
updateScript: (script: string) => {
return {
type: 'UPDATE_SCRIPT',
payload: {
script,
},
} as ActionUpdateScript
},
}
const wrapper = shallow(<IFQLPage {...props} />)

View File

@ -96,6 +96,7 @@ export const kapacitor = {
export const service = {
id: '1',
sourceID: '1',
url: 'localhost:8082',
type: 'ifql',
name: 'IFQL',
@ -108,6 +109,7 @@ export const service = {
proxy: '/chronograf/v1/sources/1/services/2/proxy',
self: '/chronograf/v1/sources/1/services/2',
},
metadata: {},
}
export const kapacitorRules = [