A few improvements to scheduling queries (#7585)

* Better message for scheduling queries

* Only allow scheduling after success

* Ask for query name and description

* Use CSS instead of <br />
This commit is contained in:
Beto Dealmeida 2019-05-23 09:15:04 -07:00 committed by GitHub
parent 421183d3f4
commit 9c8f494c9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 88 additions and 10 deletions

View File

@ -107,6 +107,17 @@ export function saveQuery(query) {
.catch(() => dispatch(addDangerToast(t('Your query could not be saved')))); .catch(() => dispatch(addDangerToast(t('Your query could not be saved'))));
} }
export function scheduleQuery(query) {
return dispatch =>
SupersetClient.post({
endpoint: '/savedqueryviewapi/api/create',
postPayload: query,
stringify: false,
})
.then(() => dispatch(addSuccessToast(t('Your query has been scheduled. To see details of your query, navigate to Saved Queries'))))
.catch(() => dispatch(addDangerToast(t('Your query could not be scheduled'))));
}
export function startQuery(query) { export function startQuery(query) {
Object.assign(query, { Object.assign(query, {
id: query.id ? query.id : shortid.generate(), id: query.id ? query.id : shortid.generate(),

View File

@ -20,6 +20,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Form from 'react-jsonschema-form'; import Form from 'react-jsonschema-form';
import chrono from 'chrono-node'; import chrono from 'chrono-node';
import { Col, FormControl, FormGroup, Row } from 'react-bootstrap';
import { t } from '@superset-ui/translation'; import { t } from '@superset-ui/translation';
import Button from '../../components/Button'; import Button from '../../components/Button';
@ -76,11 +77,17 @@ const propTypes = {
dbId: PropTypes.number.isRequired, dbId: PropTypes.number.isRequired,
animation: PropTypes.bool, animation: PropTypes.bool,
onSchedule: PropTypes.func, onSchedule: PropTypes.func,
scheduleQueryWarning: PropTypes.string,
disabled: PropTypes.bool,
tooltip: PropTypes.string,
}; };
const defaultProps = { const defaultProps = {
defaultLabel: t('Undefined'), defaultLabel: t('Undefined'),
animation: true, animation: true,
onSchedule: () => {}, onSchedule: () => {},
scheduleQueryWarning: null,
disabled: false,
tooltip: null,
}; };
class ScheduleQueryButton extends React.PureComponent { class ScheduleQueryButton extends React.PureComponent {
@ -123,12 +130,53 @@ class ScheduleQueryButton extends React.PureComponent {
} }
renderModalBody() { renderModalBody() {
return ( return (
<Form <FormGroup>
schema={getJSONSchema()} <Row style={{ paddingBottom: '10px' }}>
uiSchema={getUISchema()} <Col md={12}>
onSubmit={this.onSchedule} <label className="control-label" htmlFor="embed-height">
validate={getValidator()} {t('Label')}
/> </label>
<FormControl
type="text"
placeholder={t('Label for your query')}
value={this.state.label}
onChange={this.onLabelChange}
/>
</Col>
</Row>
<Row style={{ paddingBottom: '10px' }}>
<Col md={12}>
<label className="control-label" htmlFor="embed-height">
{t('Description')}
</label>
<FormControl
componentClass="textarea"
placeholder={t('Write a description for your query')}
value={this.state.description}
onChange={this.onDescriptionChange}
/>
</Col>
</Row>
<Row>
<Col md={12}>
<Form
schema={getJSONSchema()}
uiSchema={getUISchema()}
onSubmit={this.onSchedule}
validate={getValidator()}
/>
</Col>
</Row>
{this.props.scheduleQueryWarning && (
<Row>
<Col md={12}>
<small>
{this.props.scheduleQueryWarning}
</small>
</Col>
</Row>
)}
</FormGroup>
); );
} }
render() { render() {
@ -139,7 +187,13 @@ class ScheduleQueryButton extends React.PureComponent {
modalTitle={t('Schedule Query')} modalTitle={t('Schedule Query')}
modalBody={this.renderModalBody()} modalBody={this.renderModalBody()}
triggerNode={ triggerNode={
<Button bsSize="small" className="toggleSchedule" onClick={this.toggleSchedule}> <Button
bsSize="small"
className="toggleSchedule"
onClick={this.toggleSchedule}
disabled={this.props.disabled}
tooltip={this.props.tooltip}
>
<i className="fa fa-calendar" /> {t('Schedule Query')} <i className="fa fa-calendar" /> {t('Schedule Query')}
</Button> </Button>
} }

View File

@ -67,13 +67,14 @@ const propTypes = {
defaultQueryLimit: PropTypes.number.isRequired, defaultQueryLimit: PropTypes.number.isRequired,
maxRow: PropTypes.number.isRequired, maxRow: PropTypes.number.isRequired,
saveQueryWarning: PropTypes.string, saveQueryWarning: PropTypes.string,
scheduleQueryWarning: PropTypes.string,
}; };
const defaultProps = { const defaultProps = {
database: null, database: null,
latestQuery: null, latestQuery: null,
hideLeftBar: false, hideLeftBar: false,
saveQueryWarning: null, scheduleQueryWarning: null,
}; };
class SqlEditor extends React.PureComponent { class SqlEditor extends React.PureComponent {
@ -334,6 +335,10 @@ class SqlEditor extends React.PureComponent {
</OverlayTrigger> </OverlayTrigger>
); );
} }
const successful = this.props.latestQuery && this.props.latestQuery.state === 'success';
const scheduleToolTip = successful
? t('Schedule the query periodically')
: t('You must run the query successfully first');
return ( return (
<div className="sql-toolbar" id="js-sql-toolbar"> <div className="sql-toolbar" id="js-sql-toolbar">
<div> <div>
@ -355,9 +360,12 @@ class SqlEditor extends React.PureComponent {
defaultLabel={qe.title} defaultLabel={qe.title}
sql={qe.sql} sql={qe.sql}
className="m-r-5" className="m-r-5"
onSchedule={this.props.actions.saveQuery} onSchedule={this.props.actions.scheduleQuery}
schema={qe.schema} schema={qe.schema}
dbId={qe.dbId} dbId={qe.dbId}
scheduleQueryWarning={this.props.scheduleQueryWarning}
tooltip={scheduleToolTip}
disabled={!successful}
/> />
</span> </span>
} }

View File

@ -41,11 +41,13 @@ const propTypes = {
tables: PropTypes.array.isRequired, tables: PropTypes.array.isRequired,
offline: PropTypes.bool, offline: PropTypes.bool,
saveQueryWarning: PropTypes.string, saveQueryWarning: PropTypes.string,
scheduleQueryWarning: PropTypes.string,
}; };
const defaultProps = { const defaultProps = {
queryEditors: [], queryEditors: [],
offline: false, offline: false,
saveQueryWarning: null, saveQueryWarning: null,
scheduleQueryWarning: null,
}; };
let queryCount = 1; let queryCount = 1;
@ -250,6 +252,7 @@ class TabbedSqlEditors extends React.PureComponent {
defaultQueryLimit={this.props.defaultQueryLimit} defaultQueryLimit={this.props.defaultQueryLimit}
maxRow={this.props.maxRow} maxRow={this.props.maxRow}
saveQueryWarning={this.props.saveQueryWarning} saveQueryWarning={this.props.saveQueryWarning}
scheduleQueryWarning={this.props.scheduleQueryWarning}
/> />
)} )}
</Tab> </Tab>
@ -294,6 +297,7 @@ function mapStateToProps({ sqlLab, common }) {
defaultQueryLimit: common.conf.DEFAULT_SQLLAB_LIMIT, defaultQueryLimit: common.conf.DEFAULT_SQLLAB_LIMIT,
maxRow: common.conf.SQL_MAX_ROW, maxRow: common.conf.SQL_MAX_ROW,
saveQueryWarning: common.conf.SQLLAB_SAVE_WARNING_MESSAGE, saveQueryWarning: common.conf.SQLLAB_SAVE_WARNING_MESSAGE,
scheduleQueryWarning: common.conf.SQLLAB_SCHEDULE_WARNING_MESSAGE,
}; };
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {

View File

@ -357,8 +357,9 @@ DEFAULT_SQLLAB_LIMIT = 1000
# Maximum number of tables/views displayed in the dropdown window in SQL Lab. # Maximum number of tables/views displayed in the dropdown window in SQL Lab.
MAX_TABLE_NAMES = 3000 MAX_TABLE_NAMES = 3000
# Adds a warning message on sqllab save query modal. # Adds a warning message on sqllab save query and schedule query modals.
SQLLAB_SAVE_WARNING_MESSAGE = None SQLLAB_SAVE_WARNING_MESSAGE = None
SQLLAB_SCHEDULE_WARNING_MESSAGE = None
# If defined, shows this text in an alert-warning box in the navbar # If defined, shows this text in an alert-warning box in the navbar
# one example use case may be "STAGING" to make it clear that this is # one example use case may be "STAGING" to make it clear that this is