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';
import ReactJson from 'react-json-view';
import { previewQuery } from '@/_helpers/appUtils';
import { EventManager } from '../Inspector/EventManager';
import { CodeHinter } from '../CodeBuilder/CodeHinter';
import { DataSourceTypes } from '../DataSourceManager/SourceComponents';
const queryNameRegex = new RegExp('^[A-Za-z0-9_-]*$');
import MaximizeIcon from '../Icons/maximize.svg';
import MinimizeIcon from '../Icons/minimize.svg';
const staticDataSources = [
{ kind: 'restapi', id: 'null', name: 'REST API' },
{ kind: 'runjs', id: 'runjs', name: 'Run JavaScript code' },
];
let QueryManager = class QueryManager extends React.Component {
constructor(props) {
super(props);
this.state = {
options: {},
selectedQuery: null,
selectedDataSource: null,
dataSourceMeta: {},
};
this.previewPanelRef = React.createRef();
}
setStateFromProps = (props) => {
const selectedQuery = props.selectedQuery;
const dataSourceId = selectedQuery?.data_source_id;
const source = props.dataSources.find((datasource) => datasource.id === dataSourceId);
let dataSourceMeta = DataSourceTypes.find((source) => source.kind === selectedQuery?.kind);
const paneHeightChanged = this.state.queryPaneHeight !== props.queryPaneHeight;
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,
selectedSource: source,
dataSourceMeta,
selectedDataSource: paneHeightChanged ? this.state.selectedDataSource : props.selectedDataSource,
},
() => {
if (this.props.mode === 'edit') {
let source = props.dataSources.find((datasource) => datasource.id === selectedQuery.data_source_id);
if (selectedQuery.kind === 'restapi') {
if (!selectedQuery.data_source_id) {
source = { kind: 'restapi' };
}
}
if (selectedQuery.kind === 'runjs') {
if (!selectedQuery.data_source_id) {
source = { kind: 'runjs' };
}
}
this.setState({
options: selectedQuery.options,
selectedDataSource: source,
selectedQuery,
queryName: selectedQuery.name,
});
}
// } else {
// this.setState({
// options: {},
// selectedQuery: null,
// selectedDataSource: paneHeightChanged ? this.state.selectedDataSource : props.selectedDataSource,
// });
// }
}
);
};
componentWillReceiveProps(nextProps) {
this.setStateFromProps(nextProps);
}
componentDidMount() {
this.setStateFromProps(this.props);
}
changeDataSource = (sourceId) => {
const source = [...this.state.dataSources, ...staticDataSources].find((datasource) => datasource.id === sourceId);
const isSchemaUnavailable = ['restapi', 'stripe', 'runjs'].includes(source.kind);
const schemaUnavailableOptions = {
restapi: {
method: 'get',
url: null,
url_params: [],
headers: [],
body: [],
},
stripe: {},
runjs: {},
};
this.setState({
selectedDataSource: source,
selectedSource: source,
queryName: this.computeQueryName(source.kind),
...(isSchemaUnavailable && { options: schemaUnavailableOptions[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 === 'null' ? null : 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 (
);
};
// Here we have mocked data query in format of a component to be usable by event manager
// TODO: Refactor EventManager to be generic
mockDataQueryAsComponent = () => {
const dataQueryEvents = this.state.options?.events || [];
return {
component: { component: { definition: { events: dataQueryEvents } } },
componentMeta: {
events: {
onDataQuerySuccess: { displayName: 'Query Success' },
onDataQueryFailure: { displayName: 'Query Failure' },
},
},
};
};
eventsChanged = (events) => {
this.optionchanged('events', events);
};
renderQueryEditorIcon = () => {
if (this.state.queryPaneHeight === '30%') {
return (