| /** |
| * 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, { FunctionComponent, useState } from 'react'; |
| import SchemaForm, { FormProps, FormValidation } from 'react-jsonschema-form'; |
| import { Row, Col, Input, TextArea } from 'src/common/components'; |
| import { t, styled } from '@superset-ui/core'; |
| import * as chrono from 'chrono-node'; |
| import ModalTrigger from 'src/components/ModalTrigger'; |
| import { Form, FormItem } from 'src/components/Form'; |
| import './ScheduleQueryButton.less'; |
| import Button from 'src/components/Button'; |
| |
| const validators = { |
| greater: (a: number, b: number) => a > b, |
| greater_equal: (a: number, b: number) => a >= b, |
| less: (a: number, b: number) => a < b, |
| less_equal: (a: number, b: number) => a <= b, |
| }; |
| |
| const getJSONSchema = () => { |
| const jsonSchema = window.featureFlags.SCHEDULED_QUERIES?.JSONSCHEMA; |
| // parse date-time into usable value (eg, 'today' => `new Date()`) |
| if (jsonSchema) { |
| Object.entries(jsonSchema.properties).forEach( |
| ([key, value]: [string, any]) => { |
| if (value.default && value.format === 'date-time') { |
| jsonSchema.properties[key] = { |
| ...value, |
| default: chrono.parseDate(value.default).toISOString(), |
| }; |
| } |
| }, |
| ); |
| return jsonSchema; |
| } |
| return {}; |
| }; |
| |
| const getUISchema = () => window.featureFlags.SCHEDULED_QUERIES?.UISCHEMA; |
| |
| const getValidationRules = () => |
| window.featureFlags.SCHEDULED_QUERIES?.VALIDATION || []; |
| |
| const getValidator = () => { |
| const rules: any = getValidationRules(); |
| return (formData: Record<string, any>, errors: FormValidation) => { |
| rules.forEach((rule: any) => { |
| const test = validators[rule.name]; |
| const args = rule.arguments.map((name: string) => formData[name]); |
| const container = rule.container || rule.arguments.slice(-1)[0]; |
| if (!test(...args)) { |
| errors[container].addError(rule.message); |
| } |
| }); |
| return errors; |
| }; |
| }; |
| |
| interface ScheduleQueryButtonProps { |
| defaultLabel?: string; |
| sql: string; |
| schema?: string; |
| dbId: number; |
| animation?: boolean; |
| onSchedule?: Function; |
| scheduleQueryWarning: string | null; |
| disabled: boolean; |
| tooltip: string; |
| } |
| |
| const StyledRow = styled(Row)` |
| padding-bottom: ${({ theme }) => theme.gridUnit * 2}px; |
| `; |
| |
| export const StyledButtonComponent = styled(Button)` |
| background: none; |
| text-transform: none; |
| padding: 0px; |
| color: rgba(0, 0, 0, 0.85); |
| font-size: 14px; |
| font-weight: ${({ theme }) => theme.typography.weights.normal}; |
| &:disabled { |
| background: none; |
| color: rgba(0, 0, 0, 0.85); |
| &:hover { |
| background: none; |
| color: rgba(0, 0, 0, 0.85); |
| } |
| } |
| `; |
| |
| const ScheduleQueryButton: FunctionComponent<ScheduleQueryButtonProps> = ({ |
| defaultLabel = t('Undefined'), |
| sql, |
| schema, |
| dbId, |
| onSchedule = () => {}, |
| scheduleQueryWarning, |
| tooltip, |
| disabled = false, |
| }) => { |
| const [description, setDescription] = useState(''); |
| const [label, setLabel] = useState(defaultLabel); |
| const [showSchedule, setShowSchedule] = useState(false); |
| let saveModal: ModalTrigger | null; |
| |
| const onScheduleSubmit = ({ |
| formData, |
| }: { |
| formData: Omit<FormProps<Record<string, any>>, 'schema'>; |
| }) => { |
| const query = { |
| label, |
| description, |
| db_id: dbId, |
| schema, |
| sql, |
| extra_json: JSON.stringify({ schedule_info: formData }), |
| }; |
| onSchedule(query); |
| saveModal?.close(); |
| }; |
| |
| const renderModalBody = () => ( |
| <Form layout="vertical"> |
| <StyledRow> |
| <Col xs={24}> |
| <FormItem label={t('Label')}> |
| <Input |
| type="text" |
| placeholder={t('Label for your query')} |
| value={label} |
| onChange={(event: React.ChangeEvent<HTMLInputElement>) => |
| setLabel(event.target.value) |
| } |
| /> |
| </FormItem> |
| </Col> |
| </StyledRow> |
| <StyledRow> |
| <Col xs={24}> |
| <FormItem label={t('Description')}> |
| <TextArea |
| rows={4} |
| placeholder={t('Write a description for your query')} |
| value={description} |
| onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => |
| setDescription(event.target.value) |
| } |
| /> |
| </FormItem> |
| </Col> |
| </StyledRow> |
| <Row> |
| <Col xs={24}> |
| <div className="json-schema"> |
| <SchemaForm |
| schema={getJSONSchema()} |
| uiSchema={getUISchema} |
| onSubmit={onScheduleSubmit} |
| validate={getValidator()} |
| > |
| <Button |
| buttonStyle="primary" |
| htmlType="submit" |
| css={{ float: 'right' }} |
| > |
| Submit |
| </Button> |
| </SchemaForm> |
| </div> |
| </Col> |
| </Row> |
| {scheduleQueryWarning && ( |
| <Row> |
| <Col xs={24}> |
| <small>{scheduleQueryWarning}</small> |
| </Col> |
| </Row> |
| )} |
| </Form> |
| ); |
| |
| return ( |
| <span className="ScheduleQueryButton"> |
| <ModalTrigger |
| ref={ref => { |
| saveModal = ref; |
| }} |
| modalTitle={t('Schedule query')} |
| modalBody={renderModalBody()} |
| triggerNode={ |
| <StyledButtonComponent |
| onClick={() => setShowSchedule(!showSchedule)} |
| buttonSize="small" |
| buttonStyle="link" |
| tooltip={tooltip} |
| disabled={disabled} |
| > |
| {t('Schedule')} |
| </StyledButtonComponent> |
| } |
| /> |
| </span> |
| ); |
| }; |
| |
| export default ScheduleQueryButton; |