From aea90be7ff124dc59ab036c052711166a3f87181 Mon Sep 17 00:00:00 2001 From: YuXia Date: Tue, 8 Nov 2022 10:18:57 +0800 Subject: [PATCH 01/10] debug --- .../components/TabbedSqlEditors/index.jsx | 2 +- .../src/components/TableSelector/index.tsx | 46 +-- .../DatabaseSelector.test.tsx | 287 ++++++++++++++++++ .../src/components/TemplateSelector/index.jsx | 195 ++++++++++++ 4 files changed, 490 insertions(+), 40 deletions(-) create mode 100644 superset-frontend/src/components/TemplateSelector/DatabaseSelector.test.tsx create mode 100644 superset-frontend/src/components/TemplateSelector/index.jsx diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx index 2b52d7e8f076..2105e05bc853 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx @@ -214,7 +214,7 @@ class TabbedSqlEditors extends React.PureComponent { : this.props.defaultDbId || firstDbId, schema: activeQueryEditor ? activeQueryEditor.schema : null, autorun: false, - sql: `${warning}SELECT ...`, + sql: `${warning}SELECT ...。。。。。ss`, queryLimit: this.props.defaultQueryLimit, }; this.props.actions.addNewQueryEditor(qe); diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx index a0f6e5366bf3..5fce19d293fb 100644 --- a/superset-frontend/src/components/TableSelector/index.tsx +++ b/superset-frontend/src/components/TableSelector/index.tsx @@ -32,6 +32,9 @@ import Icons from 'src/components/Icons'; import DatabaseSelector, { DatabaseObject, } from 'src/components/DatabaseSelector'; +import TemplateSelector, { + DatabaseObject, +} from 'src/components/TemplateSelector'; import RefreshLabel from 'src/components/RefreshLabel'; import CertifiedBadge from 'src/components/CertifiedBadge'; import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip'; @@ -284,51 +287,16 @@ const TableSelector: FunctionComponent = ({ [], ); - function renderTableSelect() { - const disabled = (currentSchema && !formMode && readOnly) || !currentSchema; - - const header = sqlLabMode ? ( - {t('See table schema')} - ) : ( - {t('Table')} - ); - - const select = ( - + ) + + + return () + // return renderSelectRow() + return renderSelectRow( + , templateParams.name); + } + + return ( + + {renderTemplateSelect()} + {/*{currentTemplate && paramsList.map(item => (renderParamsInput(item)))}*/} + + ); +} From 2410cbbede547067578507a813c22f9ad62556e8 Mon Sep 17 00:00:00 2001 From: Zhen Ju Date: Tue, 8 Nov 2022 10:36:36 +0800 Subject: [PATCH 02/10] Constraint select option properties to [lable, value] to make it work --- .../src/components/TemplateSelector/index.jsx | 66 ++++++------------- 1 file changed, 21 insertions(+), 45 deletions(-) diff --git a/superset-frontend/src/components/TemplateSelector/index.jsx b/superset-frontend/src/components/TemplateSelector/index.jsx index cc5c8cad937a..a16e89aa9258 100644 --- a/superset-frontend/src/components/TemplateSelector/index.jsx +++ b/superset-frontend/src/components/TemplateSelector/index.jsx @@ -16,17 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -import React, {useState, useEffect} from 'react'; -import {styled, t} from '@superset-ui/core'; -// import {Select} from 'src/components'; -import {Input} from 'src/components/Input'; -import {Select} from 'antd'; +import React, { useState, useEffect } from 'react'; +import { styled, t } from '@superset-ui/core'; +import { Select } from 'src/components'; +// import { Select } from 'antd'; +import { Input } from 'src/components/Input'; import Label from 'src/components/Label'; -import {FormLabel} from 'src/components/Form'; +import { FormLabel } from 'src/components/Form'; const TemplateSelectorWrapper = styled.div` - ${({theme}) => ` + ${({ theme }) => ` .refresh { display: flex; align-items: center; @@ -60,7 +60,7 @@ const LabelStyle = styled.div` display: flex; flex-direction: row; align-items: center; - margin-left: ${({theme}) => theme.gridUnit - 2}px; + margin-left: ${({ theme }) => theme.gridUnit - 2}px; .backend { overflow: visible; @@ -82,7 +82,6 @@ const LabelStyle = styled.div` // ); export default function TemplateSelector() { - const [templateOptions, setTemplateOptions] = useState([]); const [paramsList, setParamsList] = useState([]); const [currentTemplate, setCurrentTemplate] = useState(null); @@ -92,15 +91,9 @@ export default function TemplateSelector() { // 获取templates const templateOptions = [ { - id: 1, + // id: 1, label: '项目各公司占比', - paramsList: [ - { - id: 0, - label: '公司名称', - name: 'company', - }, - ], + value: 'company_ratio', }, ]; // 赋值 @@ -115,6 +108,7 @@ export default function TemplateSelector() { // 触发template切换事件,然后这里要替换currenttemplate function changeTemplate(value) { + console.log('onChange', value); // 切换currenttemplate setCurrentTemplate(value); } @@ -133,40 +127,22 @@ export default function TemplateSelector() { {label} {input} - ) + ); } function renderTemplateSelect() { - console.log('debugging', templateOptions, currentTemplate) + console.log('debugging', templateOptions, currentTemplate); const tempOptions = [ { label: 'foo', - value: 'bar' + value: 'bar', }, { label: 'hello', - value: 'world' - } - ] - return ( - ) - - - return () - // return renderSelectRow() + value: 'world', + }, + ]; + return renderSelectRow( , templateParams.name); + return renderInputRow(, templateParams.name); } return ( From 00b4cdd27f81b4d34193b3823aaef74b5411a7a5 Mon Sep 17 00:00:00 2001 From: xiayu Date: Wed, 9 Nov 2022 10:54:33 +0800 Subject: [PATCH 03/10] Added run button without event binding --- .../components/SqlEditorLeftBar/index.tsx | 1 + .../src/components/TableSelector/index.tsx | 48 ++- .../DatabaseSelector.test.tsx | 287 ------------------ .../src/components/TemplateSelector/index.jsx | 119 ++++---- 4 files changed, 104 insertions(+), 351 deletions(-) delete mode 100644 superset-frontend/src/components/TemplateSelector/DatabaseSelector.test.tsx diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx index 06a31711db4a..3b810a7068b8 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx @@ -269,6 +269,7 @@ export default function SqlEditorLeftBar({ tableValue={selectedTableNames} sqlLabMode /> +
= ({ }, [], ); + function renderTableSelect() { + const disabled = (currentSchema && !formMode && readOnly) || !currentSchema; - function renderTempaletSelect() { - return ( - + const header = sqlLabMode ? ( + {t('See table schema')} + ) : ( + {t('Table')} ); + + const select = ( + , ); } function renderParamsInput(templateParams) { - return renderInputRow(, templateParams.name); + return renderInputRow( + , + templateParams.name, + ); } return ( {renderTemplateSelect()} - {/*{currentTemplate && paramsList.map(item => (renderParamsInput(item)))}*/} + {currentTemplate && paramsList.map(item => renderParamsInput(item))} ); } From fe22ee7f092be4d3e6a28cd4da1df505006e12a5 Mon Sep 17 00:00:00 2001 From: xiayu Date: Thu, 10 Nov 2022 11:29:00 +0800 Subject: [PATCH 04/10] Adjusted the code format --- .../src/components/TableSelector/index.tsx | 9 ++--- .../src/components/TemplateSelector/index.jsx | 40 ++++++++----------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx index ca33cac19c2c..41b37e4cd652 100644 --- a/superset-frontend/src/components/TableSelector/index.tsx +++ b/superset-frontend/src/components/TableSelector/index.tsx @@ -32,9 +32,7 @@ import Icons from 'src/components/Icons'; import DatabaseSelector, { DatabaseObject, } from 'src/components/DatabaseSelector'; -import TemplateSelector, { - DatabaseObject, -} from 'src/components/TemplateSelector'; +import TemplateSelector from 'src/components/TemplateSelector'; import RefreshLabel from 'src/components/RefreshLabel'; import CertifiedBadge from 'src/components/CertifiedBadge'; import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip'; @@ -339,7 +337,8 @@ const TableSelector: FunctionComponent = ({ ); }; -export const TableSelectorMultiple: FunctionComponent= - props => ; +export const TableSelectorMultiple: FunctionComponent< + TableSelectorProps +> = props => ; export default TableSelector; diff --git a/superset-frontend/src/components/TemplateSelector/index.jsx b/superset-frontend/src/components/TemplateSelector/index.jsx index d84d6577ccc5..9b2104401afa 100644 --- a/superset-frontend/src/components/TemplateSelector/index.jsx +++ b/superset-frontend/src/components/TemplateSelector/index.jsx @@ -23,7 +23,8 @@ import { Input } from 'src/components/Input'; import { FormLabel } from 'src/components/Form'; const TemplateSelectorWrapper = styled.div` - ${({ theme }) => ` .refresh { + ${({ theme }) => ` + .refresh { display: flex; align-items: center; width: 30px; @@ -40,10 +41,7 @@ const TemplateSelectorWrapper = styled.div` width: calc(100% - 30px - ${theme.gridUnit}px); flex: 1; } - .params-section { - color: 'red'; - } - + .input { width: calc(100% - 30px - ${theme.gridUnit}px); flex: 1; @@ -61,7 +59,6 @@ export default function TemplateSelector() { const [paramsList, setParamsList] = useState([]); const [currentTemplate, setCurrentTemplate] = useState(null); - // mounted useEffect(() => { const templatesInfo = [ { @@ -96,28 +93,23 @@ export default function TemplateSelector() { }, ]; setTemplatesInfo(templatesInfo); - // 获取templates - // 赋值 - const templateOptions = templatesInfo.map(item => { - return { - label: item.label, - value: item.id, - }; - }); + + const templateOptions = templatesInfo.map(item => ({ + label: item.label, + value: item.id, + })); setTemplateOptions(templateOptions); }, []); - // 监听currentTemplate useEffect(() => { if (currentTemplate) { setParamsList(templatesInfo[currentTemplate.value].paramsList); } - }, [currentTemplate]); + }, [currentTemplate, templatesInfo]); - // 触发template切换事件,然后这里要替换currentTemplate - function changeTemplate(value) { - if (value) { - setCurrentTemplate(value); + function changeTemplate(template) { + if (template) { + setCurrentTemplate(template); } } @@ -133,7 +125,7 @@ export default function TemplateSelector() { return ( <> {label} -
+
{input}
@@ -156,10 +148,10 @@ export default function TemplateSelector() { ); } - function renderParamsInput(templateParams) { + function renderParamsInput(templateParam) { return renderInputRow( - , - templateParams.name, + , + templateParam.name, ); } From b2b01855790af6f8374106e87a11105b033e1f63 Mon Sep 17 00:00:00 2001 From: xiayu Date: Thu, 10 Nov 2022 21:00:05 +0800 Subject: [PATCH 05/10] Add post request --- .../components/SqlEditorLeftBar/index.tsx | 1 - .../src/components/TemplateSelector/index.jsx | 73 +++++++++++++++++-- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx index 3b810a7068b8..06a31711db4a 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx @@ -269,7 +269,6 @@ export default function SqlEditorLeftBar({ tableValue={selectedTableNames} sqlLabMode /> -
{ + // 这里向后台发送请求,获取模板列表 const templatesInfo = [ { id: 0, @@ -93,6 +97,7 @@ export default function TemplateSelector() { }, ]; setTemplatesInfo(templatesInfo); + // 将模板转成selector需要的格式 const templateOptions = templatesInfo.map(item => ({ label: item.label, @@ -104,8 +109,15 @@ export default function TemplateSelector() { useEffect(() => { if (currentTemplate) { setParamsList(templatesInfo[currentTemplate.value].paramsList); + const payload = {}; + paramsList.forEach(item => { + payload[item.name] = null; + }); + setPayload(payload); + } else { + setPayload({}); } - }, [currentTemplate, templatesInfo]); + }, [currentTemplate, templatesInfo, paramsList]); function changeTemplate(template) { if (template) { @@ -147,18 +159,63 @@ export default function TemplateSelector() { />, ); } - + function changeParam(e) { + if (e.target && e.target.id) { + const currentPayload = payload; + currentPayload[e.target.id] = e.target.value; + setPayload(currentPayload); + } + } function renderParamsInput(templateParam) { return renderInputRow( - , + , templateParam.name, ); } + function postTemplateParamsData(payload) { + return SupersetClient.post({ + endpoint: encodeURI('/api/create_dataset'), + postPayload: payload, + }) + .then(({ json }) => { + setButtonLoading(false); + const { dataset_id: datasetId } = json; + window.open( + `/explore/?datasource_id=${datasetId}&datasource_type=query`, + '_blank', + 'noreferrer', + ); + }) + .catch(err => { + // 这里应该改成错误提示信息, + console.log(err); + }); + } + + function onClick() { + setButtonLoading(true); + postTemplateParamsData(payload); + } return ( - - {renderTemplateSelect()} - {currentTemplate && paramsList.map(item => renderParamsInput(item))} - + <> + + {renderTemplateSelect()} + {currentTemplate && paramsList.map(item => renderParamsInput(item))} + + + ); } From c08a2f1303f6df280dbc93b53d8f635fc9493bbd Mon Sep 17 00:00:00 2001 From: xiayu Date: Wed, 16 Nov 2022 14:56:32 +0800 Subject: [PATCH 06/10] Replaces the SqlEditorLeftBar component with the TemplateSelector component --- .../src/SqlLab/components/SqlEditor/index.jsx | 10 ++------ .../src/components/TableSelector/index.tsx | 7 +----- .../src/components/TemplateSelector/index.jsx | 24 ++++++++++++++----- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx b/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx index ad1dcc815834..890bae1b9929 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx +++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx @@ -75,13 +75,13 @@ import { import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { EmptyStateBig } from 'src/components/EmptyState'; import { isEmpty } from 'lodash'; +import TemplateSelector from 'src/components/TemplateSelector'; import TemplateParamsEditor from '../TemplateParamsEditor'; import ConnectedSouthPane from '../SouthPane/state'; import SaveQuery from '../SaveQuery'; import ScheduleQueryButton from '../ScheduleQueryButton'; import EstimateQueryCostButton from '../EstimateQueryCostButton'; import ShareSqlLabQuery from '../ShareSqlLabQuery'; -import SqlEditorLeftBar from '../SqlEditorLeftBar'; import AceEditorWrapper from '../AceEditorWrapper'; import RunQueryActionButton from '../RunQueryActionButton'; import QueryLimitSelect from '../QueryLimitSelect'; @@ -655,13 +655,7 @@ const SqlEditor = ({ width={adjustedWidth} hide={hideLeftBar} > - setShowEmptyState(bool)} - /> + )} diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx index 41b37e4cd652..b70e0691b3db 100644 --- a/superset-frontend/src/components/TableSelector/index.tsx +++ b/superset-frontend/src/components/TableSelector/index.tsx @@ -32,7 +32,6 @@ import Icons from 'src/components/Icons'; import DatabaseSelector, { DatabaseObject, } from 'src/components/DatabaseSelector'; -import TemplateSelector from 'src/components/TemplateSelector'; import RefreshLabel from 'src/components/RefreshLabel'; import CertifiedBadge from 'src/components/CertifiedBadge'; import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip'; @@ -323,16 +322,12 @@ const TableSelector: FunctionComponent = ({ return renderSelectRow(select, refreshLabel); } - function renderTemplateSelect() { - return ; - } return ( + {/* 这里是要隐藏掉的selector */} {renderDatabaseSelector()} {sqlLabMode && !formMode &&
} {renderTableSelect()} -
- {renderTemplateSelect()} ); }; diff --git a/superset-frontend/src/components/TemplateSelector/index.jsx b/superset-frontend/src/components/TemplateSelector/index.jsx index 5ebf6bbb114c..c1ee23918e78 100644 --- a/superset-frontend/src/components/TemplateSelector/index.jsx +++ b/superset-frontend/src/components/TemplateSelector/index.jsx @@ -62,8 +62,24 @@ export default function TemplateSelector() { const [payload, setPayload] = useState(null); const [buttonLoading, setButtonLoading] = useState(false); + // function getTemplate() { + // SupersetClient.get({ endpoint: `/api/v1/database/getTemplateList` }) + // .then(({ json }) => { + // const templatesInfo = json.template_list + // setTemplatesInfo(templatesInfo); + // // 将模板转成selector需要的格式 + // const templateOptions = templatesInfo.map(item => ({ + // label: item.label, + // value: item.id, + // })); + // setTemplateOptions(templateOptions); + // }) + // .catch(() => {}); + // } + useEffect(() => { // 这里向后台发送请求,获取模板列表 + // getTemplate(); const templatesInfo = [ { id: 0, @@ -74,10 +90,6 @@ export default function TemplateSelector() { name: 'org', description: '', }, - { - name: 'email', - description: 'a', - }, ], }, { @@ -139,7 +151,6 @@ export default function TemplateSelector() { {label}
{input} -
); @@ -170,7 +181,7 @@ export default function TemplateSelector() { return renderInputRow( , @@ -209,6 +220,7 @@ export default function TemplateSelector() { {currentTemplate && paramsList.map(item => renderParamsInput(item))} ); }; diff --git a/superset-frontend/src/components/TemplateSelector/index.jsx b/superset-frontend/src/components/TemplateSelector/index.jsx index c1ee23918e78..e6d18d081323 100644 --- a/superset-frontend/src/components/TemplateSelector/index.jsx +++ b/superset-frontend/src/components/TemplateSelector/index.jsx @@ -19,19 +19,23 @@ import React, { useState, useEffect } from 'react'; import { styled, t, SupersetClient } from '@superset-ui/core'; import { Select } from 'src/components'; +import RefreshLabel from 'src/components/RefreshLabel'; +import MultipleInput from 'src/components/MultipleInput'; import { Input } from 'src/components/Input'; -import Button from 'src/components/Button'; import { FormLabel } from 'src/components/Form'; const TemplateSelectorWrapper = styled.div` ${({ theme }) => ` - .refresh { + .add-label, .refresh { display: flex; align-items: center; width: 30px; margin-left: ${theme.gridUnit}px; margin-top: ${theme.gridUnit * 5}px; } + .add-label{ + transform: translateY(${theme.gridUnit * 5}px); + } .section { display: flex; flex-direction: row; @@ -54,82 +58,45 @@ const TemplateSelectorWrapper = styled.div` `} `; -export default function TemplateSelector() { +export default function TemplateSelector(props) { const [templatesInfo, setTemplatesInfo] = useState([]); const [templateOptions, setTemplateOptions] = useState([]); - const [paramsList, setParamsList] = useState([]); const [currentTemplate, setCurrentTemplate] = useState(null); - const [payload, setPayload] = useState(null); - const [buttonLoading, setButtonLoading] = useState(false); + const [params, setParams] = useState(null); - // function getTemplate() { - // SupersetClient.get({ endpoint: `/api/v1/database/getTemplateList` }) - // .then(({ json }) => { - // const templatesInfo = json.template_list - // setTemplatesInfo(templatesInfo); - // // 将模板转成selector需要的格式 - // const templateOptions = templatesInfo.map(item => ({ - // label: item.label, - // value: item.id, - // })); - // setTemplateOptions(templateOptions); - // }) - // .catch(() => {}); - // } + function getTemplates() { + SupersetClient.get({ endpoint: `/api/templates` }) + .then(({ json }) => { + // 判断是否合法 + const templatesInfo = json; + setTemplatesInfo(templatesInfo); + const templateOptions = templatesInfo.map((item, index) => ({ + label: item.label, + value: index, + })); + setTemplateOptions(templateOptions); + }) + .catch(err => { + console.log(err); + // 这里要改成错误提示信息 + }); + } useEffect(() => { - // 这里向后台发送请求,获取模板列表 - // getTemplate(); - const templatesInfo = [ - { - id: 0, - label: '项目各公司占比', - name: 'company_ratio', - paramsList: [ - { - name: 'org', - description: '', - }, - ], - }, - { - id: 1, - label: '项目时区占比', - name: 'timezone_ratio', - paramsList: [ - { - name: 'org', - description: '', - }, - { - name: 'email', - description: 'aa', - }, - ], - }, - ]; - setTemplatesInfo(templatesInfo); - // 将模板转成selector需要的格式 - - const templateOptions = templatesInfo.map(item => ({ - label: item.label, - value: item.id, - })); - setTemplateOptions(templateOptions); + getTemplates(); }, []); useEffect(() => { if (currentTemplate) { - setParamsList(templatesInfo[currentTemplate.value].paramsList); - const payload = {}; - paramsList.forEach(item => { - payload[item.name] = null; + const par = {}; + templatesInfo[currentTemplate.value].params.forEach(item => { + par[item.name] = null; }); - setPayload(payload); - } else { - setPayload({}); + setParams(par); + props.onTemplateChange(templatesInfo[currentTemplate.value].template_id); } - }, [currentTemplate, templatesInfo, paramsList]); + setParams({}); + }, [currentTemplate]); function changeTemplate(template) { if (template) { @@ -141,6 +108,12 @@ export default function TemplateSelector() { return (
{select} + + getTemplates()} + tooltipContent={t('Force refresh table list')} + /> +
); } @@ -148,13 +121,17 @@ export default function TemplateSelector() { function renderInputRow(input, label) { return ( <> - {label} -
+ {label} +
{input} +
); } + function renderMultipleInputRow(template, func) { + return ; + } function renderTemplateSelect() { return renderSelectRow( @@ -170,64 +147,36 @@ export default function TemplateSelector() { />, ); } - function changeParam(e) { - if (e.target && e.target.id) { - const currentPayload = payload; - currentPayload[e.target.id] = e.target.value; - setPayload(currentPayload); - } - } - function renderParamsInput(templateParam) { - return renderInputRow( - , - templateParam.name, - ); + function changeParam(id, value) { + const currentPayload = params; + currentPayload[id] = value; + setParams(currentPayload); + props.onParamsChange(params); } - function postTemplateParamsData(payload) { - return SupersetClient.post({ - endpoint: encodeURI('/api/create_dataset'), - postPayload: payload, - }) - .then(({ json }) => { - setButtonLoading(false); - const { dataset_id: datasetId } = json; - window.open( - `/explore/?datasource_id=${datasetId}&datasource_type=query`, - '_blank', - 'noreferrer', - ); - }) - .catch(err => { - // 这里应该改成错误提示信息, - console.log(err); - }); + function renderParamsInput(templateParam) { + return templateParam.description.indexOf('json数组') === -1 + ? renderInputRow( + changeParam(e.target.id, e.target.value)} + id={templateParam.name} + />, + templateParam.name, + ) + : renderMultipleInputRow(templateParam, changeParam); } - function onClick() { - setButtonLoading(true); - postTemplateParamsData(payload); - } return ( <> {renderTemplateSelect()} - {currentTemplate && paramsList.map(item => renderParamsInput(item))} + {currentTemplate && + templatesInfo[currentTemplate.value].params.map(item => + renderParamsInput(item), + )} - ); } From d85df60ba1c6ff4b22a610195e9f3ae96edc2a04 Mon Sep 17 00:00:00 2001 From: xiayu Date: Mon, 21 Nov 2022 18:06:29 +0800 Subject: [PATCH 08/10] Implemented page jumping --- .../src/components/TableSelector/index.tsx | 16 ++++++++-------- .../src/components/TemplateSelector/index.jsx | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx index 0f51ba7d95aa..07a3204d8758 100644 --- a/superset-frontend/src/components/TableSelector/index.tsx +++ b/superset-frontend/src/components/TableSelector/index.tsx @@ -42,7 +42,6 @@ import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip'; import { useToasts } from 'src/components/MessageToasts/withToasts'; import { SchemaOption } from 'src/SqlLab/types'; import { useTables, Table } from 'src/hooks/apiResources'; -import { queryEditorSetTemplateParams } from 'src/SqlLab/actions/sqlLab'; const REFRESH_WIDTH = 30; @@ -337,26 +336,27 @@ const TableSelector: FunctionComponent = ({ const [params, setParams] = useState({}); const [template_id, setTemplateId] = useState(''); const [dataset_name, setDatasetName] = useState(''); - function postTemplateParamsData(payload: object) { - console.log(payload); + function postTemplateParamsData(payload: object) { return SupersetClient.post({ - endpoint: encodeURI('/api/dataset'), - postPayload: payload, + url: 'http://192.168.8.60:5000/api/dataset', + body: JSON.stringify(payload), + headers: { 'Content-Type': 'application/json' }, + parseMethod: 'json-bigint', }) .then(({ json }) => { setButtonLoading(false); const { dataset_id: datasetId } = json; window.open( - `/explore/?datasource_id=${datasetId}&datasource_type=query`, + `/explore/?datasource_id=${datasetId}&dataset_type=table&dataset_id=${datasetId}&datasource_type=table`, '_blank', 'noreferrer', ); }) - .catch(err => { + .catch(e => { setButtonLoading(false); + console.log('error', e); // 这里应该改成错误提示信息弹出框 - console.log(err); }); } diff --git a/superset-frontend/src/components/TemplateSelector/index.jsx b/superset-frontend/src/components/TemplateSelector/index.jsx index e6d18d081323..ca73fa20bfea 100644 --- a/superset-frontend/src/components/TemplateSelector/index.jsx +++ b/superset-frontend/src/components/TemplateSelector/index.jsx @@ -63,11 +63,15 @@ export default function TemplateSelector(props) { const [templateOptions, setTemplateOptions] = useState([]); const [currentTemplate, setCurrentTemplate] = useState(null); const [params, setParams] = useState(null); + const [loadingTemplates, setLoadingTemplates] = useState(true); function getTemplates() { - SupersetClient.get({ endpoint: `/api/templates` }) + SupersetClient.get({ + url: 'http://192.168.8.60:5000/api/templates', + }) .then(({ json }) => { - // 判断是否合法 + setLoadingTemplates(false); + // 这里缺少判断是否合法 const templatesInfo = json; setTemplatesInfo(templatesInfo); const templateOptions = templatesInfo.map((item, index) => ({ @@ -76,9 +80,10 @@ export default function TemplateSelector(props) { })); setTemplateOptions(templateOptions); }) - .catch(err => { - console.log(err); - // 这里要改成错误提示信息 + .catch(e => { + setLoadingTemplates(false); + console.log('error', e); + // 这里缺少错误提示信息 }); } @@ -96,6 +101,7 @@ export default function TemplateSelector(props) { props.onTemplateChange(templatesInfo[currentTemplate.value].template_id); } setParams({}); + props.onParamsChange({}); }, [currentTemplate]); function changeTemplate(template) { @@ -142,6 +148,7 @@ export default function TemplateSelector(props) { name="select-template" placeholder={t('Select template')} onChange={changeTemplate} + loading={loadingTemplates} options={templateOptions} showSearch />, From a868b34759e8ddab006e5924111a379d358b29ae Mon Sep 17 00:00:00 2001 From: xiayu Date: Mon, 21 Nov 2022 18:57:54 +0800 Subject: [PATCH 09/10] Update templateselector key --- .../src/components/TemplateSelector/index.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/superset-frontend/src/components/TemplateSelector/index.jsx b/superset-frontend/src/components/TemplateSelector/index.jsx index ca73fa20bfea..ff528c182667 100644 --- a/superset-frontend/src/components/TemplateSelector/index.jsx +++ b/superset-frontend/src/components/TemplateSelector/index.jsx @@ -135,8 +135,8 @@ export default function TemplateSelector(props) { ); } - function renderMultipleInputRow(template, func) { - return ; + function renderMultipleInputRow(template, func, key) { + return ; } function renderTemplateSelect() { @@ -166,13 +166,17 @@ export default function TemplateSelector(props) { ? renderInputRow( changeParam(e.target.id, e.target.value)} id={templateParam.name} />, templateParam.name, ) - : renderMultipleInputRow(templateParam, changeParam); + : renderMultipleInputRow( + templateParam, + changeParam, + currentTemplate.label + templateParam.name, + ); } return ( From eef78c912a086382f20ef50595b10fd3e654771e Mon Sep 17 00:00:00 2001 From: xiayu Date: Thu, 1 Dec 2022 19:49:19 +0800 Subject: [PATCH 10/10] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E9=AA=8C=E8=AF=81=EF=BC=8C=E4=BD=86=E6=98=AF=E8=BF=98?= =?UTF-8?q?=E4=B8=8D=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/SqlLab/actions/Validator.js | 40 +++ .../components/SqlEditorLeftBar/index.tsx | 2 +- .../components/TabbedSqlEditors/index.jsx | 2 +- .../SqlLab/components/TableSelector/index.tsx | 340 ++++++++++++++++++ .../src/components/TableSelector/index.tsx | 117 +----- .../src/components/TemplateSelector/index.jsx | 8 +- 6 files changed, 394 insertions(+), 115 deletions(-) create mode 100644 superset-frontend/src/SqlLab/actions/Validator.js create mode 100644 superset-frontend/src/SqlLab/components/TableSelector/index.tsx diff --git a/superset-frontend/src/SqlLab/actions/Validator.js b/superset-frontend/src/SqlLab/actions/Validator.js new file mode 100644 index 000000000000..b7eb534c9c63 --- /dev/null +++ b/superset-frontend/src/SqlLab/actions/Validator.js @@ -0,0 +1,40 @@ +const strategies = { + isNonEmpty(value, errMsg) { + if (value === '') { + return errMsg; + } + }, + minLenth(value, length, errMsg) { + if (value.length < length) { + return errMsg; + } + }, + isMobile(value, errMsg) { + if (!/^1[3|5|8][0-9]{9}$/.test(value)) { + return errMsg; + } + }, +}; + +export default class Validator { + constructor() { + this.cache = []; + } + + add(value, rule, errMsg) { + const arr = rule.split(':'); + this.cache.push(() => { + const strategy = arr.shift(); + arr.unshift(value); + arr.push(errMsg); + return strategies[strategy](value, errMsg); + }); + } + + start() { + for (let i = 0; i < this.cache.length; i++) { + const msg = this.cache[i](); + if (msg) return msg; + } + } +} diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx index 06a31711db4a..0f9d8d0e3673 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx @@ -30,7 +30,7 @@ import Button from 'src/components/Button'; import { t, styled, css, SupersetTheme } from '@superset-ui/core'; import Collapse from 'src/components/Collapse'; import Icons from 'src/components/Icons'; -import { TableSelectorMultiple } from 'src/components/TableSelector'; +import { TableSelectorMultiple } from 'src/SqlLab/components/TableSelector'; import { IconTooltip } from 'src/components/IconTooltip'; import { QueryEditor, SchemaOption } from 'src/SqlLab/types'; import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor'; diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx index 2105e05bc853..2b52d7e8f076 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx @@ -214,7 +214,7 @@ class TabbedSqlEditors extends React.PureComponent { : this.props.defaultDbId || firstDbId, schema: activeQueryEditor ? activeQueryEditor.schema : null, autorun: false, - sql: `${warning}SELECT ...。。。。。ss`, + sql: `${warning}SELECT ...`, queryLimit: this.props.defaultQueryLimit, }; this.props.actions.addNewQueryEditor(qe); diff --git a/superset-frontend/src/SqlLab/components/TableSelector/index.tsx b/superset-frontend/src/SqlLab/components/TableSelector/index.tsx new file mode 100644 index 000000000000..9e44216b049d --- /dev/null +++ b/superset-frontend/src/SqlLab/components/TableSelector/index.tsx @@ -0,0 +1,340 @@ +/** + * 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, + ReactNode, + useEffect, +} from 'react'; +import { Modal } from 'antd'; +import { styled, t, SupersetClient } from '@superset-ui/core'; +import { FormLabel } from 'src/components/Form'; +import Icons from 'src/components/Icons'; +import DatabaseSelector, { + DatabaseObject, +} from 'src/components/DatabaseSelector'; +import TemplateSelector from 'src/components/TemplateSelector'; +import Button from 'src/components/Button'; +import { Input } from 'src/components/Input'; +import CertifiedBadge from 'src/components/CertifiedBadge'; +import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip'; +import { useToasts } from 'src/components/MessageToasts/withToasts'; +import { SchemaOption } from 'src/SqlLab/types'; +import { useTables, Table } from 'src/hooks/apiResources'; +import Validator from 'src/SqlLab/actions/Validator'; + +const REFRESH_WIDTH = 30; + +const TableSelectorWrapper = styled.div` + ${({ theme }) => ` + .refresh { + display: flex; + align-items: center; + width: ${REFRESH_WIDTH}px; + margin-left: ${theme.gridUnit}px; + margin-top: ${theme.gridUnit * 5}px; + } + + .section { + display: flex; + flex-direction: row; + align-items: center; + } + + .input { + width: calc(100% - 30px - ${theme.gridUnit}px); + flex: 1; + } + .divider { + border-bottom: 1px solid ${theme.colors.secondary.light5}; + margin: 15px 0; + } + .table-length { + color: ${theme.colors.grayscale.light1}; + } + .select { + flex: 1; + max-width: calc(100% - ${theme.gridUnit + REFRESH_WIDTH}px) + } + & > div { + margin-bottom: ${theme.gridUnit * 4}px; + } + `} +`; + +const TableLabel = styled.span` + align-items: center; + display: flex; + white-space: nowrap; + + svg, + small { + margin-right: ${({ theme }) => theme.gridUnit}px; + } +`; + +interface TableSelectorProps { + clearable?: boolean; + database?: DatabaseObject | null; + emptyState?: ReactNode; + formMode?: boolean; + getDbList?: (arg0: any) => {}; + handleError: (msg: string) => void; + isDatabaseSelectEnabled?: boolean; + onDbChange?: (db: DatabaseObject) => void; + onSchemaChange?: (schema?: string) => void; + onSchemasLoad?: (schemaOptions: SchemaOption[]) => void; + onTablesLoad?: (options: Array) => void; + readOnly?: boolean; + schema?: string; + onEmptyResults?: (searchText?: string) => void; + sqlLabMode?: boolean; + tableValue?: string | string[]; + onTableSelectChange?: (value?: string | string[], schema?: string) => void; + tableSelectMode?: 'single' | 'multiple'; +} + +export interface TableOption { + label: JSX.Element; + text: string; + value: string; +} + +export const TableOption = ({ table }: { table: Table }) => { + const { value, type, extra } = table; + return ( + + {type === 'view' ? ( + + ) : ( + + )} + {extra?.certification && ( + + )} + {extra?.warning_markdown && ( + + )} + {value} + + ); +}; + +const TableSelector: FunctionComponent = ({ + database, + emptyState, + formMode = false, + getDbList, + handleError, + isDatabaseSelectEnabled = true, + onDbChange, + onSchemaChange, + onSchemasLoad, + readOnly = false, + onEmptyResults, + schema, + sqlLabMode = true, +}) => { + const { addSuccessToast } = useToasts(); + const [currentSchema, setCurrentSchema] = useState( + schema, + ); + + useEffect(() => { + if (database === undefined) { + setCurrentSchema(undefined); + } + }, [database]); + + const internalDbChange = (db: DatabaseObject) => { + if (onDbChange) { + onDbChange(db); + } + }; + + const internalSchemaChange = (schema?: string) => { + setCurrentSchema(schema); + if (onSchemaChange) { + onSchemaChange(schema); + } + }; + + function renderDatabaseSelector() { + return ( + + ); + } + + const [buttonLoading, setButtonLoading] = useState(false); + const [params, setParams] = useState({}); + const [template_id, setTemplateId] = useState(''); + const [dataset_name, setDatasetName] = useState(''); + const [datasetId, setDatasetId] = useState(null); + + function postTemplateParamsData(payload: object) { + const modal = Modal.info({ + content: 'Generating dataset ... ...', + okButtonProps: { + disabled: true, + loading: true, + }, + okText: 'chart', + }); + return SupersetClient.post({ + url: 'http://192.168.8.69:5000/api/dataset', + body: JSON.stringify(payload), + headers: { 'Content-Type': 'application/json' }, + }) + .then(({ json }) => { + setButtonLoading(false); + const { dataset_id: datasetId } = json; + setDatasetId(datasetId); + modal.update({ + content: 'Dataset created successfully', + okButtonProps: { + disabled: false, + loading: false, + }, + okText: 'chart', + onOk: () => { + handleOk(); + }, + }); + }) + .catch(e => { + setButtonLoading(false); + modal.update({ + content: 'Failed to create dataset.', + okButtonProps: { + disabled: false, + loading: false, + }, + okText: 'cancel', + }); + }); + } + function handleOk() { + window.open( + `/explore/?datasource_id=${datasetId}&dataset_type=table&dataset_id=${datasetId}&datasource_type=table`, + '_blank', + 'noreferrer', + ); + } + + function createDataset() { + setButtonLoading(true); + const validator = new Validator(); + validator.add(dataset_name, 'isNonEmpty', 'dataset name 不能为空'); + validator.add(currentSchema, 'isNonEmpty', '请选择Schema'); + validator.add(database?.id, 'isNonEmpty', '请选择数据库'); + const errMsg = validator.start(); + if (errMsg) { + Modal.confirm({ + content: errMsg, + }); + return; + } + postTemplateParamsData({ + database: database?.id, + schema: currentSchema, + params, + template_id, + dataset_name, + }); + } + function onParamsChange(params: Object) { + setParams(params); + } + function onTemplateChange(id: string) { + setTemplateId(id); + } + + function renderInputRow(input: ReactNode, label: string) { + return ( + <> + {label} +
+ {input} + +
+ + ); + } + function DatasetNameChange(value: string) { + if (value) { + setDatasetName(value); + } + } + + return ( + + {renderDatabaseSelector()} + {sqlLabMode && !formMode &&
} + + {renderInputRow( + { + DatasetNameChange(e.target.value); + }} + />, + 'dataset name', + )} + + + ); +}; + +export const TableSelectorMultiple: FunctionComponent< + TableSelectorProps +> = props => ; + +export default TableSelector; diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx index 07a3204d8758..29bd7668457d 100644 --- a/superset-frontend/src/components/TableSelector/index.tsx +++ b/superset-frontend/src/components/TableSelector/index.tsx @@ -25,17 +25,13 @@ import React, { } from 'react'; import { SelectValue } from 'antd/lib/select'; -import { styled, t, SupersetClient } from '@superset-ui/core'; +import { styled, t } from '@superset-ui/core'; import { Select } from 'src/components'; import { FormLabel } from 'src/components/Form'; import Icons from 'src/components/Icons'; import DatabaseSelector, { DatabaseObject, } from 'src/components/DatabaseSelector'; -import TemplateSelector from 'src/components/TemplateSelector'; -import Button from 'src/components/Button'; -import { Input } from 'src/components/Input'; - import RefreshLabel from 'src/components/RefreshLabel'; import CertifiedBadge from 'src/components/CertifiedBadge'; import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip'; @@ -54,17 +50,11 @@ const TableSelectorWrapper = styled.div` margin-left: ${theme.gridUnit}px; margin-top: ${theme.gridUnit * 5}px; } - .section { display: flex; flex-direction: row; align-items: center; } - - .input { - width: calc(100% - 30px - ${theme.gridUnit}px); - flex: 1; - } .divider { border-bottom: 1px solid ${theme.colors.secondary.light5}; margin: 15px 0; @@ -76,9 +66,6 @@ const TableSelectorWrapper = styled.div` flex: 1; max-width: calc(100% - ${theme.gridUnit + REFRESH_WIDTH}px) } - & > div { - margin-bottom: ${theme.gridUnit * 4}px; - } `} `; @@ -86,7 +73,6 @@ const TableLabel = styled.span` align-items: center; display: flex; white-space: nowrap; - svg, small { margin-right: ${({ theme }) => theme.gridUnit}px; @@ -203,10 +189,10 @@ const TableSelector: FunctionComponent = ({ () => data ? data.options.map(table => ({ - value: table.value, - label: , - text: table.value, - })) + value: table.value, + label: , + text: table.value, + })) : [], [data], ); @@ -292,6 +278,7 @@ const TableSelector: FunctionComponent = ({ }, [], ); + function renderTableSelect() { const disabled = (currentSchema && !formMode && readOnly) || !currentSchema; @@ -332,100 +319,16 @@ const TableSelector: FunctionComponent = ({ return renderSelectRow(select, refreshLabel); } - const [buttonLoading, setButtonLoading] = useState(false); - const [params, setParams] = useState({}); - const [template_id, setTemplateId] = useState(''); - const [dataset_name, setDatasetName] = useState(''); - - function postTemplateParamsData(payload: object) { - return SupersetClient.post({ - url: 'http://192.168.8.60:5000/api/dataset', - body: JSON.stringify(payload), - headers: { 'Content-Type': 'application/json' }, - parseMethod: 'json-bigint', - }) - .then(({ json }) => { - setButtonLoading(false); - const { dataset_id: datasetId } = json; - window.open( - `/explore/?datasource_id=${datasetId}&dataset_type=table&dataset_id=${datasetId}&datasource_type=table`, - '_blank', - 'noreferrer', - ); - }) - .catch(e => { - setButtonLoading(false); - console.log('error', e); - // 这里应该改成错误提示信息弹出框 - }); - } - - function createDataset() { - setButtonLoading(true); - // 传递之前应当还有一个校验,检验参数值是否为空的过程 - postTemplateParamsData({ - database: database?.id, - schema: currentSchema, - params, - template_id, - dataset_name, - }); - } - function onParamsChange(params: Object) { - setParams(params); - } - function onTemplateChange(id: string) { - setTemplateId(id); - } - - function renderInputRow(input: ReactNode, label: string) { - return ( - <> - {label} -
- {input} - -
- - ); - } - function DatasetNameChange(value: string) { - if (value) { - setDatasetName(value); - } - } - return ( {renderDatabaseSelector()} {sqlLabMode && !formMode &&
} - - {renderInputRow( - { - DatasetNameChange(e.target.value); - }} - />, - 'dataset name', - )} - + {renderTableSelect()} ); }; -export const TableSelectorMultiple: FunctionComponent< - TableSelectorProps -> = props => ; +export const TableSelectorMultiple: FunctionComponent = + props => ; -export default TableSelector; +export default TableSelector; \ No newline at end of file diff --git a/superset-frontend/src/components/TemplateSelector/index.jsx b/superset-frontend/src/components/TemplateSelector/index.jsx index ff528c182667..6840f5115991 100644 --- a/superset-frontend/src/components/TemplateSelector/index.jsx +++ b/superset-frontend/src/components/TemplateSelector/index.jsx @@ -64,14 +64,12 @@ export default function TemplateSelector(props) { const [currentTemplate, setCurrentTemplate] = useState(null); const [params, setParams] = useState(null); const [loadingTemplates, setLoadingTemplates] = useState(true); - function getTemplates() { SupersetClient.get({ url: 'http://192.168.8.60:5000/api/templates', }) .then(({ json }) => { setLoadingTemplates(false); - // 这里缺少判断是否合法 const templatesInfo = json; setTemplatesInfo(templatesInfo); const templateOptions = templatesInfo.map((item, index) => ({ @@ -80,10 +78,9 @@ export default function TemplateSelector(props) { })); setTemplateOptions(templateOptions); }) - .catch(e => { + .catch(() => { setLoadingTemplates(false); - console.log('error', e); - // 这里缺少错误提示信息 + props.handleError(t('There was an error loading the templates')); }); } @@ -167,7 +164,6 @@ export default function TemplateSelector(props) { changeParam(e.target.id, e.target.value)} id={templateParam.name} />, templateParam.name,