Persist ifql script
Co-authored-by: Andrew Watkins <andrew.watkinz@gmail.com> Co-authored-by: Chris Henn <chris.henn@influxdata.com>ifql/save-service
parent
b778fd9756
commit
4326855844
|
@ -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},
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
import AJAX from 'src/utils/ajax'
|
import AJAX from 'src/utils/ajax'
|
||||||
|
import {Service} from 'src/types'
|
||||||
|
import {updateService} from 'src/shared/apis'
|
||||||
|
|
||||||
export const getSuggestions = async (url: string) => {
|
export const getSuggestions = async (url: string) => {
|
||||||
try {
|
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 {
|
try {
|
||||||
const data = await AJAX({
|
const data = await AJAX({
|
||||||
method: 'POST',
|
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
|
return data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Problem fetching data', error)
|
console.error('Problem fetching data', error)
|
||||||
throw error
|
throw error.data.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,3 +91,19 @@ export const getTagValues = async () => {
|
||||||
throw error
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -58,3 +58,6 @@ export const EXCLUDED_KEYS = [
|
||||||
'Meta',
|
'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'
|
||||||
|
|
|
@ -10,11 +10,14 @@ import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
|
||||||
|
|
||||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||||
import {analyzeSuccess} from 'src/shared/copy/notifications'
|
import {analyzeSuccess} from 'src/shared/copy/notifications'
|
||||||
|
import {
|
||||||
|
updateScript as updateScriptAction,
|
||||||
|
UpdateScript,
|
||||||
|
} from 'src/ifql/actions'
|
||||||
|
|
||||||
import {bodyNodes} from 'src/ifql/helpers'
|
import {bodyNodes} from 'src/ifql/helpers'
|
||||||
import {getSuggestions, getAST, getTimeSeries} from 'src/ifql/apis'
|
import {getSuggestions, getAST, getTimeSeries} from 'src/ifql/apis'
|
||||||
import {builder, argTypes} from 'src/ifql/constants'
|
import {funcNames, builder, argTypes} from 'src/ifql/constants'
|
||||||
import {funcNames} from 'src/ifql/constants'
|
|
||||||
|
|
||||||
import {Source, Service, Notification} from 'src/types'
|
import {Source, Service, Notification} from 'src/types'
|
||||||
import {
|
import {
|
||||||
|
@ -37,6 +40,11 @@ interface Props {
|
||||||
services: Service[]
|
services: Service[]
|
||||||
sources: Source[]
|
sources: Source[]
|
||||||
notify: (message: Notification) => void
|
notify: (message: Notification) => void
|
||||||
|
script: string
|
||||||
|
updateScript: UpdateScript
|
||||||
|
params: {
|
||||||
|
sourceID: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Body extends FlatBody {
|
interface Body extends FlatBody {
|
||||||
|
@ -46,7 +54,6 @@ interface Body extends FlatBody {
|
||||||
interface State {
|
interface State {
|
||||||
body: Body[]
|
body: Body[]
|
||||||
ast: object
|
ast: object
|
||||||
script: string
|
|
||||||
data: string
|
data: string
|
||||||
suggestions: Suggestion[]
|
suggestions: Suggestion[]
|
||||||
status: Status
|
status: Status
|
||||||
|
@ -63,7 +70,6 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
ast: null,
|
ast: null,
|
||||||
data: 'Hit "Get Data!" or Ctrl + Enter to run your script',
|
data: 'Hit "Get Data!" or Ctrl + Enter to run your script',
|
||||||
suggestions: [],
|
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: {
|
status: {
|
||||||
type: 'none',
|
type: 'none',
|
||||||
text: '',
|
text: '',
|
||||||
|
@ -72,7 +78,7 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async componentDidMount() {
|
public async componentDidMount() {
|
||||||
const {links} = this.props
|
const {links, script} = this.props
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const suggestions = await getSuggestions(links.suggestions)
|
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)
|
console.error('Could not get function suggestions: ', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getASTResponse(this.state.script)
|
this.getASTResponse(script)
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {suggestions, script, data, body, status} = this.state
|
const {suggestions, data, body, status} = this.state
|
||||||
|
const {script} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CheckServices>
|
<CheckServices>
|
||||||
|
@ -140,7 +147,7 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleSubmitScript = () => {
|
private handleSubmitScript = () => {
|
||||||
this.getASTResponse(this.state.script)
|
this.getASTResponse(this.props.script)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleGenerateScript = (): void => {
|
private handleGenerateScript = (): void => {
|
||||||
|
@ -260,21 +267,21 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAppendFrom = (): void => {
|
private handleAppendFrom = (): void => {
|
||||||
const {script} = this.state
|
const {script} = this.props
|
||||||
const newScript = `${script.trim()}\n\n${builder.NEW_FROM}\n\n`
|
const newScript = `${script.trim()}\n\n${builder.NEW_FROM}\n\n`
|
||||||
|
|
||||||
this.getASTResponse(newScript)
|
this.getASTResponse(newScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAppendJoin = (): void => {
|
private handleAppendJoin = (): void => {
|
||||||
const {script} = this.state
|
const {script} = this.props
|
||||||
const newScript = `${script.trim()}\n\n${builder.NEW_JOIN}\n\n`
|
const newScript = `${script.trim()}\n\n${builder.NEW_JOIN}\n\n`
|
||||||
|
|
||||||
this.getASTResponse(newScript)
|
this.getASTResponse(newScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleChangeScript = (script: string): void => {
|
private handleChangeScript = (script: string): void => {
|
||||||
this.setState({script})
|
this.props.updateScript(script)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAddNode = (
|
private handleAddNode = (
|
||||||
|
@ -376,10 +383,10 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAnalyze = async () => {
|
private handleAnalyze = async () => {
|
||||||
const {links, notify} = this.props
|
const {links, notify, script} = this.props
|
||||||
|
|
||||||
try {
|
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 body = bodyNodes(ast, this.state.suggestions)
|
||||||
const status = {type: 'success', text: ''}
|
const status = {type: 'success', text: ''}
|
||||||
notify(analyzeSuccess)
|
notify(analyzeSuccess)
|
||||||
|
@ -411,7 +418,8 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
})
|
})
|
||||||
const body = bodyNodes(ast, suggestions)
|
const body = bodyNodes(ast, suggestions)
|
||||||
const status = {type: 'success', text: ''}
|
const status = {type: 'success', text: ''}
|
||||||
this.setState({ast, script, body, status})
|
this.setState({ast, body, status})
|
||||||
|
this.props.updateScript(script)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.setState({status: this.parseError(error)})
|
this.setState({status: this.parseError(error)})
|
||||||
return console.error('Could not parse AST', error)
|
return console.error('Could not parse AST', error)
|
||||||
|
@ -419,14 +427,14 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTimeSeries = async () => {
|
private getTimeSeries = async () => {
|
||||||
const {script} = this.state
|
const {script} = this.props
|
||||||
this.setState({data: 'fetching data...'})
|
this.setState({data: 'fetching data...'})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {data} = await getTimeSeries(script)
|
const {data} = await getTimeSeries(this.service, script)
|
||||||
this.setState({data})
|
this.setState({data})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.setState({data: 'Error fetching data'})
|
this.setState({data: error})
|
||||||
console.error('Could not get timeSeries', error)
|
console.error('Could not get timeSeries', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,12 +448,13 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = ({links, services, sources}) => {
|
const mapStateToProps = ({links, services, sources, script}) => {
|
||||||
return {links: links.ifql, services, sources}
|
return {links: links.ifql, services, sources, script}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
notify: notifyAction,
|
notify: notifyAction,
|
||||||
|
updateScript: updateScriptAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(IFQLPage)
|
export default connect(mapStateToProps, mapDispatchToProps)(IFQLPage)
|
||||||
|
|
|
@ -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
|
|
@ -56,6 +56,7 @@ export const saveToLocalStorage = ({
|
||||||
timeRange,
|
timeRange,
|
||||||
dataExplorer,
|
dataExplorer,
|
||||||
dashTimeV1: {ranges},
|
dashTimeV1: {ranges},
|
||||||
|
script,
|
||||||
}: LocalStorage): void => {
|
}: LocalStorage): void => {
|
||||||
try {
|
try {
|
||||||
const appPersisted = {app: {persisted}}
|
const appPersisted = {app: {persisted}}
|
||||||
|
@ -70,6 +71,7 @@ export const saveToLocalStorage = ({
|
||||||
dashTimeV1,
|
dashTimeV1,
|
||||||
dataExplorer,
|
dataExplorer,
|
||||||
dataExplorerQueryConfigs,
|
dataExplorerQueryConfigs,
|
||||||
|
script,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import overlayTechnology from 'src/shared/reducers/overlayTechnology'
|
||||||
import dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
|
import dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
|
||||||
import persistStateEnhancer from './persistStateEnhancer'
|
import persistStateEnhancer from './persistStateEnhancer'
|
||||||
import servicesReducer from 'src/shared/reducers/services'
|
import servicesReducer from 'src/shared/reducers/services'
|
||||||
|
import scriptReducer from 'src/ifql/reducers/script'
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
...statusReducers,
|
...statusReducers,
|
||||||
|
@ -30,6 +31,7 @@ const rootReducer = combineReducers({
|
||||||
dashTimeV1,
|
dashTimeV1,
|
||||||
routing: routerReducer,
|
routing: routerReducer,
|
||||||
services: servicesReducer,
|
services: servicesReducer,
|
||||||
|
script: scriptReducer,
|
||||||
})
|
})
|
||||||
|
|
||||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
|
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
|
||||||
|
|
|
@ -7,6 +7,7 @@ export interface LocalStorage {
|
||||||
dataExplorer: DataExplorer
|
dataExplorer: DataExplorer
|
||||||
dataExplorerQueryConfigs: DataExplorerQueryConfigs
|
dataExplorerQueryConfigs: DataExplorerQueryConfigs
|
||||||
timeRange: TimeRange
|
timeRange: TimeRange
|
||||||
|
script: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VERSION = string
|
export type VERSION = string
|
||||||
|
|
|
@ -10,6 +10,7 @@ export interface NewService {
|
||||||
|
|
||||||
export interface Service {
|
export interface Service {
|
||||||
id?: string
|
id?: string
|
||||||
|
sourceID: string
|
||||||
url: string
|
url: string
|
||||||
name: string
|
name: string
|
||||||
type: string
|
type: string
|
||||||
|
@ -17,6 +18,9 @@ export interface Service {
|
||||||
password?: string
|
password?: string
|
||||||
active: boolean
|
active: boolean
|
||||||
insecureSkipVerify: boolean
|
insecureSkipVerify: boolean
|
||||||
|
metadata: {
|
||||||
|
[x: string]: any
|
||||||
|
}
|
||||||
links: {
|
links: {
|
||||||
source: string
|
source: string
|
||||||
self: string
|
self: string
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {shallow} from 'enzyme'
|
||||||
|
|
||||||
import {IFQLPage} from 'src/ifql/containers/IFQLPage'
|
import {IFQLPage} from 'src/ifql/containers/IFQLPage'
|
||||||
import TimeMachine from 'src/ifql/components/TimeMachine'
|
import TimeMachine from 'src/ifql/components/TimeMachine'
|
||||||
|
import {ActionUpdateScript} from 'src/ifql/actions'
|
||||||
|
|
||||||
jest.mock('src/ifql/apis', () => require('mocks/ifql/apis'))
|
jest.mock('src/ifql/apis', () => require('mocks/ifql/apis'))
|
||||||
|
|
||||||
|
@ -15,7 +16,19 @@ const setup = () => {
|
||||||
},
|
},
|
||||||
services: [],
|
services: [],
|
||||||
sources: [],
|
sources: [],
|
||||||
|
script: '',
|
||||||
notify: () => {},
|
notify: () => {},
|
||||||
|
params: {
|
||||||
|
sourceID: '',
|
||||||
|
},
|
||||||
|
updateScript: (script: string) => {
|
||||||
|
return {
|
||||||
|
type: 'UPDATE_SCRIPT',
|
||||||
|
payload: {
|
||||||
|
script,
|
||||||
|
},
|
||||||
|
} as ActionUpdateScript
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapper = shallow(<IFQLPage {...props} />)
|
const wrapper = shallow(<IFQLPage {...props} />)
|
||||||
|
|
|
@ -96,6 +96,7 @@ export const kapacitor = {
|
||||||
|
|
||||||
export const service = {
|
export const service = {
|
||||||
id: '1',
|
id: '1',
|
||||||
|
sourceID: '1',
|
||||||
url: 'localhost:8082',
|
url: 'localhost:8082',
|
||||||
type: 'ifql',
|
type: 'ifql',
|
||||||
name: 'IFQL',
|
name: 'IFQL',
|
||||||
|
@ -108,6 +109,7 @@ export const service = {
|
||||||
proxy: '/chronograf/v1/sources/1/services/2/proxy',
|
proxy: '/chronograf/v1/sources/1/services/2/proxy',
|
||||||
self: '/chronograf/v1/sources/1/services/2',
|
self: '/chronograf/v1/sources/1/services/2',
|
||||||
},
|
},
|
||||||
|
metadata: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const kapacitorRules = [
|
export const kapacitorRules = [
|
||||||
|
|
Loading…
Reference in New Issue