From 49876d12e0ea332c44562e4c1f620d41a0ca83a5 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 14 May 2019 13:33:04 -0700 Subject: [PATCH 1/2] Validate start/end when scheduling queries --- docs/installation.rst | 20 +++++++- superset/assets/package-lock.json | 13 +++++ superset/assets/package.json | 1 + .../SqlLab/components/ScheduleQueryButton.jsx | 50 ++++++++++++++++++- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index c7c24fc1d4e67..b32e9dccbb88b 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -858,13 +858,19 @@ To allow scheduled queries, add the following to your `config.py`: }, 'start_date': { 'type': 'string', - 'format': 'date-time', 'title': 'Start date', + # date-time is parsed using the Sugar library, see + # https://sugarjs.com/dates/#/Parsing for examples + 'format': 'date-time', + 'default': 'tomorrow at 9am', }, 'end_date': { 'type': 'string', - 'format': 'date-time', 'title': 'End date', + # date-time is parsed using the Sugar library, see + # https://sugarjs.com/dates/#/Parsing for examples + 'format': 'date-time', + 'default': '9am in 30 days', }, 'schedule_interval': { 'type': 'string', @@ -890,6 +896,16 @@ To allow scheduled queries, add the following to your `config.py`: ), }, }, + 'VALIDATION': [ + # ensure that start_date <= end_date + { + 'name': 'less_equal', + 'arguments': ['start_date', 'end_date'], + 'message': 'End date cannot be before start date', + # this is where the error message is shown + 'container': 'end_date', + }, + ], }, } diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index e5075d9699665..d3f30a4dcf534 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -19809,6 +19809,19 @@ } } }, + "sugar": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/sugar/-/sugar-2.0.6.tgz", + "integrity": "sha512-s0P2/pjJtAD9VA44+2Gqm3NdC4v+08melA6YubOxzshu628krTbn95/M2GWMrI9rYspZMpYBIrChR46fjQ7xsQ==", + "requires": { + "sugar-core": "^2.0.0" + } + }, + "sugar-core": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/sugar-core/-/sugar-core-2.0.6.tgz", + "integrity": "sha512-YmLFysR3Si6RImqL1+aB6JH81EXxvXn5iXhPf2PsjfoUYEwCxFDYCQY+zC3WqviuGWzxFaSkkJvkUE05Y03L5Q==" + }, "supercluster": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-4.1.1.tgz", diff --git a/superset/assets/package.json b/superset/assets/package.json index 6edb971331dc9..beed5839493d4 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -137,6 +137,7 @@ "redux-thunk": "^2.1.0", "redux-undo": "^1.0.0-beta9-9-7", "shortid": "^2.2.6", + "sugar": "^2.0.6", "underscore": "^1.8.3", "urijs": "^1.18.10", "viewport-mercator-project": "^6.1.1" diff --git a/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx b/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx index 2e7e16e3167af..b868fccaff268 100644 --- a/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx +++ b/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx @@ -19,11 +19,56 @@ import React from 'react'; import PropTypes from 'prop-types'; import Form from 'react-jsonschema-form'; +import Sugar from 'sugar'; import { t } from '@superset-ui/translation'; import Button from '../../components/Button'; import ModalTrigger from '../../components/ModalTrigger'; +const validators = { + greater: (a, b) => a > b, + greater_equal: (a, b) => a >= b, + less: (a, b) => a < b, + less_equal: (a, b) => a <= b, +}; + +function getJSONSchema() { + const jsonSchema = window.featureFlags.SCHEDULED_QUERIES.JSONSCHEMA; + // parse date-time into usable value (eg, 'today' => `new Date()`) + Object.entries(jsonSchema.properties).forEach(([key, properties]) => { + if (properties.default && properties.format === 'date-time') { + jsonSchema.properties[key] = { + ...properties, + default: Sugar.Date.format(Sugar.Date.create(properties.default), 'ISO8601'), + }; + } + }); + return jsonSchema; +} + +function getUISchema() { + return window.featureFlags.SCHEDULED_QUERIES.UISCHEMA; +} + +function getValidationRules() { + return window.featureFlags.SCHEDULED_QUERIES.VALIDATION || []; +} + +function getValidator() { + const rules = getValidationRules(); + return (formData, errors) => { + rules.forEach((rule) => { + const test = validators[rule.name]; + const args = rule.arguments.map(name => formData[name]); + const container = rule.container || rule.arguments.slice(-1)[0]; + if (!test(...args)) { + errors[container].addError(rule.message); + } + }); + return errors; + }; +} + const propTypes = { defaultLabel: PropTypes.string, sql: PropTypes.string.isRequired, @@ -79,9 +124,10 @@ class ScheduleQueryButton extends React.PureComponent { renderModalBody() { return (
); } From fc8670a9e4a4fcf79c634609871030a758321646 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Thu, 16 May 2019 10:07:29 -0700 Subject: [PATCH 2/2] Use chrono instead of Sugar --- docs/installation.rst | 8 +++--- superset/assets/package-lock.json | 28 ++++++++++--------- superset/assets/package.json | 2 +- .../SqlLab/components/ScheduleQueryButton.jsx | 4 +-- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index b32e9dccbb88b..82459c7c53eea 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -859,16 +859,16 @@ To allow scheduled queries, add the following to your `config.py`: 'start_date': { 'type': 'string', 'title': 'Start date', - # date-time is parsed using the Sugar library, see - # https://sugarjs.com/dates/#/Parsing for examples + # date-time is parsed using the chrono library, see + # https://www.npmjs.com/package/chrono-node#usage 'format': 'date-time', 'default': 'tomorrow at 9am', }, 'end_date': { 'type': 'string', 'title': 'End date', - # date-time is parsed using the Sugar library, see - # https://sugarjs.com/dates/#/Parsing for examples + # date-time is parsed using the chrono library, see + # https://www.npmjs.com/package/chrono-node#usage 'format': 'date-time', 'default': '9am in 30 days', }, diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index d3f30a4dcf534..da886a0c95d17 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -5662,6 +5662,21 @@ "tslib": "^1.9.0" } }, + "chrono-node": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-1.3.11.tgz", + "integrity": "sha512-jDWRnY6nYvzfV3HPYBqo+tot7tcsUs9i3arGbMdI0TouPSXP2C2y/Ctp27rxKTQDi6yuTxAB2cw+Q6igGhOhdQ==", + "requires": { + "moment": "2.21.0" + }, + "dependencies": { + "moment": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz", + "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==" + } + } + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -19809,19 +19824,6 @@ } } }, - "sugar": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/sugar/-/sugar-2.0.6.tgz", - "integrity": "sha512-s0P2/pjJtAD9VA44+2Gqm3NdC4v+08melA6YubOxzshu628krTbn95/M2GWMrI9rYspZMpYBIrChR46fjQ7xsQ==", - "requires": { - "sugar-core": "^2.0.0" - } - }, - "sugar-core": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/sugar-core/-/sugar-core-2.0.6.tgz", - "integrity": "sha512-YmLFysR3Si6RImqL1+aB6JH81EXxvXn5iXhPf2PsjfoUYEwCxFDYCQY+zC3WqviuGWzxFaSkkJvkUE05Y03L5Q==" - }, "supercluster": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-4.1.1.tgz", diff --git a/superset/assets/package.json b/superset/assets/package.json index beed5839493d4..52e82b12e01a1 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -84,6 +84,7 @@ "bootstrap": "^3.3.6", "bootstrap-slider": "^10.0.0", "brace": "^0.11.1", + "chrono-node": "^1.3.11", "classnames": "^2.2.5", "d3-array": "^1.2.4", "d3-color": "^1.2.0", @@ -137,7 +138,6 @@ "redux-thunk": "^2.1.0", "redux-undo": "^1.0.0-beta9-9-7", "shortid": "^2.2.6", - "sugar": "^2.0.6", "underscore": "^1.8.3", "urijs": "^1.18.10", "viewport-mercator-project": "^6.1.1" diff --git a/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx b/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx index b868fccaff268..99ecaa5256052 100644 --- a/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx +++ b/superset/assets/src/SqlLab/components/ScheduleQueryButton.jsx @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Form from 'react-jsonschema-form'; -import Sugar from 'sugar'; +import chrono from 'chrono-node'; import { t } from '@superset-ui/translation'; import Button from '../../components/Button'; @@ -39,7 +39,7 @@ function getJSONSchema() { if (properties.default && properties.format === 'date-time') { jsonSchema.properties[key] = { ...properties, - default: Sugar.Date.format(Sugar.Date.create(properties.default), 'ISO8601'), + default: chrono.parseDate(properties.default).toISOString(), }; } });