mirror of https://github.com/apache/superset.git
* Merge lastest from master into lyft-release-sp8 (#7405)
* filter out all nan series (#7313)
* improve not rich tooltip (#7345)
* Create issue_label_bot.yaml (#7341)
* fix: do not save colors without a color scheme (#7347)
* [wtforms] Strip leading/trailing whitespace (#7084)
* [schema] Updating the datasources schema (#5451)
* limit tables/views returned if schema is not provided (#7358)
* limit tables/views returned if schema is not provided
* fix typo
* improve code performance
* handle the case when table name or view name does not present a schema
* Add type anno (#7342)
* Updated local dev instructions to include missing step
* First pass at type annotations
* [schema] Updating the base column schema (#5452)
* Update 937d04c16b64_update_datasources.py (#7361)
* Feature flag for client cache (#7348)
* Feature flag for client cache
* Fix integration test
* Revert "Fix integration test"
This reverts commit 58434ab98a
.
* Feature flag for client cache
* Fix integration tests
* Add feature flag to config.py
* Add another feature check
* Fix more integration tests
* Fix raw HTML in SliceAdder (#7338)
* remove backendSync.json (#7331)
* [bubbles] issue when using duplicated metrics (#7087)
* SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04 (#7359)
* SUPERSET-8: Update text in docs copyright footer (#7360)
* SUPERSET-7: Docker compose config version breaks on Ubuntu 16.04
* SUPERSET-8: Extra text in docs copyright footer
* [schema] Adding commits and removing unnecessary foreign-key definitions (#7371)
* Store last selected dashboard in sessionStorage (#7181)
* Store last selected dashboard in sessionStorage
* Fix tests
* [schema] Updating the base metric schema (#5453)
* Fix NoneType bug & fill the test recipients with original recipients if empty (#7365)
* feat: see Presto row and array data types (#7391)
* feat: see Presto row and array data types
* fix: address PR comments
* fix: lint and build issues
* fix: add types
* Incorporate feedback from initial PR (prematurely merged to lyft-release-sp8) (#7415)
* add stronger type hints where possible
* fix: lint issues and add select_star func in Hive
* add missing pkg init
* fix: build issues
* fix: pylint issues
* fix: use logging instead of print
* feat: view presto row objects in data grid
* fix: address feedback
* fix: spacing
* Workaround for no results returned (#7442)
* feat: view presto row objects in data grid (#7436)
* feat: view presto row objects in data grid
* fix: address feedback
* fix: spacing
* feat: Scheduling queries from SQL Lab (#7416)
* Lightweight pipelines POC
* Add docs
* Minor fixes
* Remove Lyft URL
* Use enum
* Minor fix
* Fix unit tests
* Mark props as required
This commit is contained in:
parent
88e6ec992c
commit
5cf454b464
|
@ -811,6 +811,84 @@ in this dictionary are made available for users to use in their SQL.
|
|||
'my_crazy_macro': lambda x: x*2,
|
||||
}
|
||||
|
||||
**Scheduling queries**
|
||||
|
||||
You can optionally allow your users to schedule queries directly in SQL Lab.
|
||||
This is done by addding extra metadata to saved queries, which are then picked
|
||||
up by an external scheduled (like [Apache Airflow](https://airflow.apache.org/)).
|
||||
|
||||
To allow scheduled queries, add the following to your `config.py`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
FEATURE_FLAGS = {
|
||||
# Configuration for scheduling queries from SQL Lab. This information is
|
||||
# collected when the user clicks "Schedule query", and saved into the `extra`
|
||||
# field of saved queries.
|
||||
# See: https://github.com/mozilla-services/react-jsonschema-form
|
||||
'SCHEDULED_QUERIES': {
|
||||
'JSONSCHEMA': {
|
||||
'title': 'Schedule',
|
||||
'description': (
|
||||
'In order to schedule a query, you need to specify when it '
|
||||
'should start running, when it should stop running, and how '
|
||||
'often it should run. You can also optionally specify '
|
||||
'dependencies that should be met before the query is '
|
||||
'executed. Please read the documentation for best practices '
|
||||
'and more information on how to specify dependencies.'
|
||||
),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'output_table': {
|
||||
'type': 'string',
|
||||
'title': 'Output table name',
|
||||
},
|
||||
'start_date': {
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
'title': 'Start date',
|
||||
},
|
||||
'end_date': {
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
'title': 'End date',
|
||||
},
|
||||
'schedule_interval': {
|
||||
'type': 'string',
|
||||
'title': 'Schedule interval',
|
||||
},
|
||||
'dependencies': {
|
||||
'type': 'array',
|
||||
'title': 'Dependencies',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'UISCHEMA': {
|
||||
'schedule_interval': {
|
||||
'ui:placeholder': '@daily, @weekly, etc.',
|
||||
},
|
||||
'dependencies': {
|
||||
'ui:help': (
|
||||
'Check the documentation for the correct format when '
|
||||
'defining dependencies.'
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
This feature flag is based on [react-jsonschema-form](https://github.com/mozilla-services/react-jsonschema-form),
|
||||
and will add a button called "Schedule Query" to SQL Lab. When the button is
|
||||
clicked, a modal will show up where the user can add the metadata required for
|
||||
scheduling the query.
|
||||
|
||||
This information can then be retrieved from the endpoint `/savedqueryviewapi/api/read`
|
||||
and used to schedule the queries that have `scheduled_queries` in their JSON
|
||||
metadata. For schedulers other than Airflow, additional fields can be easily
|
||||
added to the configuration file above.
|
||||
|
||||
Celery Flower
|
||||
-------------
|
||||
|
|
|
@ -5791,8 +5791,7 @@
|
|||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
|
||||
},
|
||||
"coa": {
|
||||
"version": "2.0.2",
|
||||
|
@ -6071,8 +6070,7 @@
|
|||
"core-js": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.0.tgz",
|
||||
"integrity": "sha512-kLRC6ncVpuEW/1kwrOXYX6KQASCVtrh1gQr/UiaVgFlf9WE5Vp+lNe5+h3LuMr5PAucWnnEXwH0nQHRH/gpGtw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-kLRC6ncVpuEW/1kwrOXYX6KQASCVtrh1gQr/UiaVgFlf9WE5Vp+lNe5+h3LuMr5PAucWnnEXwH0nQHRH/gpGtw=="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -13233,6 +13231,11 @@
|
|||
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.topath": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
|
||||
"integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak="
|
||||
},
|
||||
"lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||
|
@ -17458,6 +17461,41 @@
|
|||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz",
|
||||
"integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA=="
|
||||
},
|
||||
"react-jsonschema-form": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.2.0.tgz",
|
||||
"integrity": "sha512-rR77qoFiQ5TxDYwsJz8UWmDner4jQ4xMnDqeV6Nvg7GtoEyOUoTVkI/SBMEzfXuF/piWZXYjquP96Hy/2L7C+Q==",
|
||||
"requires": {
|
||||
"ajv": "^5.2.3",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"core-js": "^2.5.7",
|
||||
"lodash.topath": "^4.5.2",
|
||||
"prop-types": "^15.5.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
||||
"requires": {
|
||||
"co": "^4.6.0",
|
||||
"fast-deep-equal": "^1.0.0",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
|
||||
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
|
||||
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
|
|
|
@ -117,6 +117,7 @@
|
|||
"react-dom": "^16.4.1",
|
||||
"react-gravatar": "^2.6.1",
|
||||
"react-hot-loader": "^4.3.6",
|
||||
"react-jsonschema-form": "^1.2.0",
|
||||
"react-map-gl": "^4.0.10",
|
||||
"react-markdown": "^3.3.0",
|
||||
"react-redux": "^5.0.2",
|
||||
|
|
|
@ -41,10 +41,18 @@ class QueryAutoRefresh extends React.PureComponent {
|
|||
const { queries, queriesLastUpdate } = this.props;
|
||||
const now = new Date().getTime();
|
||||
|
||||
// due to a race condition, queries can be marked as successful before the
|
||||
// results key is set; this is a workaround until we fix the underlying
|
||||
// problem
|
||||
const isQueryRunning = q => (
|
||||
['running', 'started', 'pending', 'fetching'].indexOf(q.state) >= 0 ||
|
||||
(q.state === 'success' && q.resultsKey === null)
|
||||
);
|
||||
|
||||
return (
|
||||
queriesLastUpdate > 0 &&
|
||||
Object.values(queries).some(
|
||||
q => ['running', 'started', 'pending', 'fetching'].indexOf(q.state) >= 0 &&
|
||||
q => isQueryRunning(q) &&
|
||||
now - q.startDttm < MAX_QUERY_AGE_TO_POLL,
|
||||
)
|
||||
);
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Form from 'react-jsonschema-form';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import Button from '../../components/Button';
|
||||
import ModalTrigger from '../../components/ModalTrigger';
|
||||
|
||||
const propTypes = {
|
||||
defaultLabel: PropTypes.string,
|
||||
sql: PropTypes.string.isRequired,
|
||||
schema: PropTypes.string.isRequired,
|
||||
dbId: PropTypes.number.isRequired,
|
||||
animation: PropTypes.bool,
|
||||
onSchedule: PropTypes.func,
|
||||
};
|
||||
const defaultProps = {
|
||||
defaultLabel: t('Undefined'),
|
||||
animation: true,
|
||||
onSchedule: () => {},
|
||||
};
|
||||
|
||||
class ScheduleQueryButton extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
description: '',
|
||||
label: props.defaultLabel,
|
||||
showSchedule: false,
|
||||
};
|
||||
this.toggleSchedule = this.toggleSchedule.bind(this);
|
||||
this.onSchedule = this.onSchedule.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
this.onLabelChange = this.onLabelChange.bind(this);
|
||||
this.onDescriptionChange = this.onDescriptionChange.bind(this);
|
||||
}
|
||||
onSchedule({ formData }) {
|
||||
const query = {
|
||||
label: this.state.label,
|
||||
description: this.state.description,
|
||||
db_id: this.props.dbId,
|
||||
schema: this.props.schema,
|
||||
sql: this.props.sql,
|
||||
extra_json: JSON.stringify({ schedule_info: formData }),
|
||||
};
|
||||
this.props.onSchedule(query);
|
||||
this.saveModal.close();
|
||||
}
|
||||
onCancel() {
|
||||
this.saveModal.close();
|
||||
}
|
||||
onLabelChange(e) {
|
||||
this.setState({ label: e.target.value });
|
||||
}
|
||||
onDescriptionChange(e) {
|
||||
this.setState({ description: e.target.value });
|
||||
}
|
||||
toggleSchedule(e) {
|
||||
this.setState({ target: e.target, showSchedule: !this.state.showSchedule });
|
||||
}
|
||||
renderModalBody() {
|
||||
return (
|
||||
<Form
|
||||
schema={window.featureFlags.SCHEDULED_QUERIES.JSONSCHEMA}
|
||||
uiSchema={window.featureFlags.SCHEDULED_QUERIES.UISCHEMA}
|
||||
onSubmit={this.onSchedule}
|
||||
/>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<span className="ScheduleQueryButton">
|
||||
<ModalTrigger
|
||||
ref={(ref) => { this.saveModal = ref; }}
|
||||
modalTitle={t('Schedule Query')}
|
||||
modalBody={this.renderModalBody()}
|
||||
triggerNode={
|
||||
<Button bsSize="small" className="toggleSchedule" onClick={this.toggleSchedule}>
|
||||
<i className="fa fa-calendar" /> {t('Schedule Query')}
|
||||
</Button>
|
||||
}
|
||||
bsSize="medium"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
ScheduleQueryButton.propTypes = propTypes;
|
||||
ScheduleQueryButton.defaultProps = defaultProps;
|
||||
|
||||
export default ScheduleQueryButton;
|
|
@ -36,6 +36,7 @@ import LimitControl from './LimitControl';
|
|||
import TemplateParamsEditor from './TemplateParamsEditor';
|
||||
import SouthPane from './SouthPane';
|
||||
import SaveQuery from './SaveQuery';
|
||||
import ScheduleQueryButton from './ScheduleQueryButton';
|
||||
import ShareSqlLabQuery from './ShareSqlLabQuery';
|
||||
import Timer from '../../components/Timer';
|
||||
import Hotkeys from '../../components/Hotkeys';
|
||||
|
@ -43,6 +44,7 @@ import SqlEditorLeftBar from './SqlEditorLeftBar';
|
|||
import AceEditorWrapper from './AceEditorWrapper';
|
||||
import { STATE_BSSTYLE_MAP } from '../constants';
|
||||
import RunQueryActionButton from './RunQueryActionButton';
|
||||
import { FeatureFlag, isFeatureEnabled } from '../../featureFlags';
|
||||
|
||||
const SQL_EDITOR_PADDING = 10;
|
||||
const SQL_TOOLBAR_HEIGHT = 51;
|
||||
|
@ -313,6 +315,18 @@ class SqlEditor extends React.PureComponent {
|
|||
sql={this.state.sql}
|
||||
/>
|
||||
</span>
|
||||
{isFeatureEnabled(FeatureFlag.SCHEDULED_QUERIES) &&
|
||||
<span className="m-r-5">
|
||||
<ScheduleQueryButton
|
||||
defaultLabel={qe.title}
|
||||
sql={qe.sql}
|
||||
className="m-r-5"
|
||||
onSchedule={this.props.actions.saveQuery}
|
||||
schema={qe.schema}
|
||||
dbId={qe.dbId}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
<span className="m-r-5">
|
||||
<SaveQuery
|
||||
defaultLabel={qe.title}
|
||||
|
|
|
@ -22,6 +22,7 @@ export enum FeatureFlag {
|
|||
SCOPED_FILTER = 'SCOPED_FILTER',
|
||||
OMNIBAR = 'OMNIBAR',
|
||||
CLIENT_CACHE = 'CLIENT_CACHE',
|
||||
SCHEDULED_QUERIES = 'SCHEDULED_QUERIES',
|
||||
}
|
||||
|
||||
export type FeatureFlagMap = {
|
||||
|
@ -39,5 +40,5 @@ export function initFeatureFlags(featureFlags: FeatureFlagMap) {
|
|||
}
|
||||
|
||||
export function isFeatureEnabled(feature: FeatureFlag) {
|
||||
return !!window.featureFlags[feature];
|
||||
return window && window.featureFlags && !!window.featureFlags[feature];
|
||||
}
|
||||
|
|
|
@ -116,9 +116,10 @@ class SavedQueryView(SupersetModelView, DeleteMixin):
|
|||
|
||||
class SavedQueryViewApi(SavedQueryView):
|
||||
list_columns = [
|
||||
'label', 'sqlalchemy_uri', 'user_email', 'schema', 'description',
|
||||
'sql']
|
||||
show_columns = ['label', 'db_id', 'schema', 'description', 'sql']
|
||||
'id', 'label', 'sqlalchemy_uri', 'user_email', 'schema', 'description',
|
||||
'sql', 'extra_json']
|
||||
show_columns = [
|
||||
'label', 'db_id', 'schema', 'description', 'sql', 'extra_json']
|
||||
add_columns = show_columns
|
||||
edit_columns = add_columns
|
||||
|
||||
|
|
Loading…
Reference in New Issue