import React from 'react'; import { dataqueryService } from '@/_services'; import { toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import SelectSearch, { fuzzySearch } from 'react-select-search'; import ReactTooltip from 'react-tooltip'; import { allSources } from './QueryEditors'; import { Transformation } from './Transformation'; const queryNameRegex = new RegExp('^[A-Za-z0-9_-]*$'); const staticDataSources = [{ kind: 'restapi', id: 'restapi', name: 'REST API' }]; const defaultOptions = { postgresql: {}, redis: { query: 'PING' }, mysql: {}, firestore: { path: '' }, elasticsearch: { query: '' }, restapi: { method: 'GET', url: null, url_params: [['', '']], headers: [['', '']], body: [['', '']] }, stripe: {}, googlesheets: { operation: 'read' } }; class QueryManager extends React.Component { constructor(props) { super(props); this.state = {}; } setStateFromProps = (props) => { const selectedQuery = props.selectedQuery; this.setState( { appId: props.appId, dataSources: props.dataSources, dataQueries: props.dataQueries, mode: props.mode, currentTab: 1, addingQuery: props.addingQuery, editingQuery: props.editingQuery, queryPaneHeight: props.queryPaneHeight, currentState: props.currentState }, () => { if (this.props.mode === 'edit') { const source = props.dataSources.find((datasource) => datasource.id === selectedQuery.data_source_id); this.setState({ options: selectedQuery.options, selectedDataSource: source, selectedQuery, queryName: selectedQuery.name }); } else { this.setState({ options: {}, selectedDataSource: null, selectedQuery: null }); } } ); }; componentWillReceiveProps(nextProps) { this.setStateFromProps(nextProps); } componentDidMount() { this.setStateFromProps(this.props); } changeDataSource = (sourceId) => { const source = [...this.state.dataSources, ...staticDataSources].find((datasource) => datasource.id === sourceId); this.setState({ selectedDataSource: source, options: defaultOptions[source.kind], queryName: this.computeQueryName(source.kind) }); }; switchCurrentTab = (tab) => { this.setState({ currentTab: tab }); }; validateQueryName = () => { const { queryName, dataQueries, mode, selectedQuery } = this.state; if (mode === 'create') { return dataQueries.find((query) => query.name === queryName) === undefined && queryNameRegex.test(queryName); } const existingQuery = dataQueries.find((query) => query.name === queryName); if (existingQuery) { return existingQuery.id === selectedQuery.id && queryNameRegex.test(queryName); } return queryNameRegex.test(queryName); }; computeQueryName = (kind) => { const { dataQueries } = this.state; const currentQueriesForKind = dataQueries.filter((query) => query.kind === kind); let found = false; let newName = ''; let currentNumber = currentQueriesForKind.length + 1; while (!found) { newName = `${kind}${currentNumber}`; if (dataQueries.find((query) => query.name === newName) === undefined) { found = true; } currentNumber += 1; } return newName; }; createOrUpdateDataQuery = () => { const { appId, options, selectedDataSource, mode, queryName } = this.state; const kind = selectedDataSource.kind; const dataSourceId = selectedDataSource.id; const isQueryNameValid = this.validateQueryName(); if (!isQueryNameValid) { toast.error('Invalid query name. Should be unique and only include letters, numbers and underscore.', { hideProgressBar: true, position: 'bottom-center' }); return; } if (mode === 'edit') { this.setState({ isUpdating: true }); dataqueryService.update(this.state.selectedQuery.id, queryName, options).then(() => { toast.success('Query Updated', { hideProgressBar: true, position: 'bottom-center' }); this.setState({ isUpdating: false }); this.props.dataQueriesChanged(); }).catch((error) => { this.setState({ isUpdating: false }); toast.error(error, { hideProgressBar: true, position: 'bottom-center' }); }); } else { this.setState({ isCreating: true }); dataqueryService.create(appId, queryName, kind, options, dataSourceId).then(() => { toast.success('Query Added', { hideProgressBar: true, position: 'bottom-center' }); this.setState({ isCreating: false }); this.props.dataQueriesChanged(); }).catch((error) => { this.setState({ isCreating: false }); toast.error(error, { hideProgressBar: true, position: 'bottom-center' }); }); } }; optionchanged = (option, value) => { this.setState({ options: { ...this.state.options, [option]: value } }); }; optionsChanged = (newOptions) => { this.setState({ options: newOptions }); }; toggleOption = (option) => { const currentValue = this.state.options[option] ? this.state.options[option] : false; this.optionchanged(option, !currentValue); }; renderDataSourceOption = (props, option, snapshot, className) => { return ( ); }; render() { const { dataSources, selectedDataSource, mode, currentTab, isUpdating, isCreating, addingQuery, editingQuery, selectedQuery, queryPaneHeight, currentState, queryName } = this.state; let ElementToRender = ''; if (selectedDataSource) { const sourcecomponentName = selectedDataSource.kind.charAt(0).toUpperCase() + selectedDataSource.kind.slice(1); ElementToRender = allSources[sourcecomponentName]; } let buttonText = mode === 'edit' ? 'Save' : 'Create'; const buttonDisabled = isUpdating || isCreating; if (isUpdating) { buttonText = 'Saving...'; } if (isCreating) { buttonText = 'Creating...'; } return (