From 7092ea3f9703f58066d14969ead8311c1018c2f8 Mon Sep 17 00:00:00 2001 From: yqwoe Date: Thu, 26 Oct 2023 09:29:42 +0800 Subject: [PATCH 1/5] fix: shortcut key problem; editor theme switching problem; editor full screen; database field length; (#2436) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: use-service hooks * fix: 快捷键问题;编辑器主题切换问题;编辑 器全屏;数据库字段长度; * fix:提交 .mvn * fix:提交 .idea * fix:提交 .gitignore * fix:提交 maven-wrapper.jar * fix:提交 .gitignore --- .gitignore | 2 +- dinky-admin/src/main/resources/db/db-h2.sql | 2 +- dinky-web/package.json | 1 + dinky-web/src/app.tsx | 54 +++--- .../components/CallBackButton/CircleBtn.tsx | 2 +- .../CustomEditor/CodeEdit/index.tsx | 2 +- .../CustomEditor/CodeShow/index.tsx | 2 +- dinky-web/src/global.less | 6 + dinky-web/src/hooks/useEditor.tsx | 42 +++++ .../DataStudio/FooterContainer/index.tsx | 2 +- .../DataStudio/HeaderContainer/index.tsx | 6 +- .../MiddleContainer/Editor/index.tsx | 159 ++++++++++++------ .../MiddleContainer/KeyBoard/constant.tsx | 22 +-- .../DataStudio/MiddleContainer/index.tsx | 6 +- dinky-web/src/pages/DataStudio/index.tsx | 94 ++++++----- dinky-web/src/pages/DataStudio/route.tsx | 2 + dinky-web/src/utils/function.tsx | 69 ++++---- script/sql/dinky-mysql.sql | 2 +- script/sql/dinky-pg.sql | 4 +- 19 files changed, 306 insertions(+), 173 deletions(-) create mode 100644 dinky-web/src/hooks/useEditor.tsx diff --git a/.gitignore b/.gitignore index 6a0f999ccd..097d72e48c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ logs.zip # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* .idea/* -!.idea/icon.png +!.idea/icon.svg !.idea/vcs.xml build target/* diff --git a/dinky-admin/src/main/resources/db/db-h2.sql b/dinky-admin/src/main/resources/db/db-h2.sql index df44c1f58e..abb7d7dc94 100644 --- a/dinky-admin/src/main/resources/db/db-h2.sql +++ b/dinky-admin/src/main/resources/db/db-h2.sql @@ -1642,7 +1642,7 @@ CREATE TABLE `dinky_metrics` ( `position` int(11) DEFAULT null, `show_type` varchar(255) DEFAULT null, `show_size` varchar(255) DEFAULT null, - `title` varchar(255) DEFAULT null, + `title` CLOB DEFAULT null, `layout_name` varchar(255) DEFAULT null, `create_time` datetime DEFAULT null, `update_time` datetime DEFAULT null diff --git a/dinky-web/package.json b/dinky-web/package.json index 4115635c05..e43df772c6 100644 --- a/dinky-web/package.json +++ b/dinky-web/package.json @@ -70,6 +70,7 @@ "redux-persist": "^6.0.0", "remark-gfm": "^4.0.0", "screenfull": "^6.0.2", + "sql-formatter": "^13.0.1", "styled-components": "^6.0.8", "use-sse": "^2.0.1" }, diff --git a/dinky-web/src/app.tsx b/dinky-web/src/app.tsx index f0b998e7ef..2d541078f7 100644 --- a/dinky-web/src/app.tsx +++ b/dinky-web/src/app.tsx @@ -31,6 +31,7 @@ import { persistReducer, persistStore } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import { Navigate } from 'umi'; import { default as defaultSettings, default as Settings } from '../config/defaultSettings'; +import { FullScreenProvider } from './hooks/useEditor'; import { errorConfig } from './requestErrorConfig'; import { getDataByParamsReturnResult } from './services/BusinessCrud'; import { API } from './services/data'; @@ -114,6 +115,39 @@ export async function getInitialState(): Promise<{ // ProLayout 支持的api https://procomponents.ant.design/components/layout export const layout: RunTimeLayoutConfig = ({ initialState }) => { + console.log(initialState); + const fullscreen = initialState?.fullscreen; + + const defaultSettings = { + onPageChange: () => { + const { location } = history; + // 如果没有登录,重定向到 login + if (!initialState?.currentUser && location.pathname !== loginPath) { + history.push(loginPath); + } + }, + // 自定义 403 页面 + unAccessible: , + // 增加一个 loading 的状态 + childrenRender: (children) => { + return initialState?.loading ? ( + + ) : ( + + {children} + + ); + } + }; + + if (fullscreen) { + return { + ...initialState?.settings, + siderWidth: 0, + ...defaultSettings, + layout: 'side' + }; + } return { headerTitleRender: () => { // 重新对 title 的设置进行设置 @@ -135,25 +169,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => { theme === THEME.light || undefined ? 'rgba(0, 0, 0, 0.15)' : 'rgba(255, 255, 255, 0.15)' },*/ isChildrenLayout: true, - onPageChange: () => { - const { location } = history; - // 如果没有登录,重定向到 login - if (!initialState?.currentUser && location.pathname !== loginPath) { - history.push(loginPath); - } - }, - // 自定义 403 页面 - unAccessible: , - // 增加一个 loading 的状态 - childrenRender: (children) => { - return initialState?.loading ? ( - - ) : ( - - {children} - - ); - }, + ...defaultSettings, ...initialState?.settings }; }; diff --git a/dinky-web/src/components/CallBackButton/CircleBtn.tsx b/dinky-web/src/components/CallBackButton/CircleBtn.tsx index ab383b6a55..caa5c29308 100644 --- a/dinky-web/src/components/CallBackButton/CircleBtn.tsx +++ b/dinky-web/src/components/CallBackButton/CircleBtn.tsx @@ -16,7 +16,7 @@ * */ -import { TabsItemType } from '@/pages/DataStudio/model'; +import { TabsItemType } from '@/pages/DataStudio/types'; import { Button } from 'antd'; import React from 'react'; diff --git a/dinky-web/src/components/CustomEditor/CodeEdit/index.tsx b/dinky-web/src/components/CustomEditor/CodeEdit/index.tsx index bbb276a6b5..f181efac46 100644 --- a/dinky-web/src/components/CustomEditor/CodeEdit/index.tsx +++ b/dinky-web/src/components/CustomEditor/CodeEdit/index.tsx @@ -141,7 +141,7 @@ const CodeEdit = (props: CodeEditFormProps) => { className={'editor-develop'} onMount={editorDidMount} onChange={onChange} - theme={theme ? theme : convertCodeEditTheme()} + theme={convertCodeEditTheme()} /> {showFloatButton && } diff --git a/dinky-web/src/components/CustomEditor/CodeShow/index.tsx b/dinky-web/src/components/CustomEditor/CodeShow/index.tsx index 2a5703dc13..1deda9f39d 100644 --- a/dinky-web/src/components/CustomEditor/CodeShow/index.tsx +++ b/dinky-web/src/components/CustomEditor/CodeShow/index.tsx @@ -242,7 +242,7 @@ const CodeShow = (props: CodeShowFormProps) => { } }} onMount={editorDidMount} - theme={theme ? theme : convertCodeEditTheme()} + theme={convertCodeEditTheme()} /> {/* float button */} diff --git a/dinky-web/src/global.less b/dinky-web/src/global.less index e216d1849d..894f5cc25e 100644 --- a/dinky-web/src/global.less +++ b/dinky-web/src/global.less @@ -399,6 +399,12 @@ h5 { scrollbar-width: thin; } +.editor-full-screen{ + .ant-tabs-tabpane{ + height: 100vh; + } +} + .data-studio-tabs { .ant-tabs-nav { .ant-tabs-nav-wrap { diff --git a/dinky-web/src/hooks/useEditor.tsx b/dinky-web/src/hooks/useEditor.tsx new file mode 100644 index 0000000000..2f3dc8654b --- /dev/null +++ b/dinky-web/src/hooks/useEditor.tsx @@ -0,0 +1,42 @@ +import { + createContext, + Dispatch, + FC, + memo, + SetStateAction, + useContext, + useEffect, + useState +} from 'react'; +import { flushSync } from 'react-dom'; +import { useModel } from 'umi'; +export interface FullScreenContextProps { + fullscreen: boolean; + setFullscreen: Dispatch>; +} + +export const FullScreenContext = createContext( + {} as FullScreenContextProps +); + +export const FullScreenProvider: FC = memo(({ children }) => { + const [fullscreen, setFullscreen] = useState(false); + + const { initialState, setInitialState } = useModel('@@initialState'); + + useEffect(() => { + flushSync(() => { + setInitialState((s) => ({ + ...s, + fullscreen + })); + }); + }, [fullscreen]); + return ( + + {children} + + ); +}); + +export const useEditor = () => useContext(FullScreenContext); diff --git a/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx b/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx index e783b0086a..8a204e6b48 100644 --- a/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx @@ -20,7 +20,7 @@ import useThemeValue from '@/hooks/useThemeValue'; import JobRunningModal from '@/pages/DataStudio/FooterContainer/JobRunningModal'; import { getCurrentTab } from '@/pages/DataStudio/function'; -import { StateType, TabsPageType, VIEW } from '@/pages/DataStudio/model'; +import { StateType,TabsPageType,VIEW} from '@/pages/DataStudio/model'; import { getSseData } from '@/services/api'; import { l } from '@/utils/intl'; import { connect } from '@@/exports'; diff --git a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx index 19098c325d..33887af2b1 100644 --- a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx @@ -196,8 +196,8 @@ const HeaderContainer = (props: any) => { const routes: ButtonRoute[] = [ // 保存按钮 icon { - hotKey: (e: KeyboardEvent) => e.ctrlKey && e.key === 's', - hotKeyDesc: 'Ctrl+S', + hotKey: (e: KeyboardEvent) => e.ctrlKey && e.key === 's' || e.metaKey && e.key === 's', + hotKeyDesc: 'Ctrl/Command +S', isShow: projectCommonShow(currentTab?.type), icon: , title: l('button.save'), @@ -216,6 +216,8 @@ const HeaderContainer = (props: any) => { { // 检查 sql按钮 icon: , + hotKey: (e: KeyboardEvent) => e.altKey && e.code === 'Digit2' || e.altKey && e.key === '@', + hotKeyDesc: 'Alt+2/@', title: l('pages.datastudio.editor.check'), click: () => showExplain(), isShow: projectCommonShow(currentTab?.type) diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx index ca55e9407e..12664ae814 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx @@ -28,12 +28,16 @@ import { } from '@/pages/DataStudio/model'; import { JOB_LIFE_CYCLE } from '@/pages/DevOps/constants'; import { API_CONSTANTS } from '@/services/endpoints'; +import { convertCodeEditTheme } from '@/utils/function'; import { l } from '@/utils/intl'; import { connect, useRequest } from '@@/exports'; +import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons'; import { Editor } from '@monaco-editor/react'; -import { Spin } from 'antd'; -import { editor } from 'monaco-editor'; +import { Button, Spin } from 'antd'; +import { editor, KeyCode, KeyMod } from 'monaco-editor'; import React, { useState } from 'react'; +import { format } from 'sql-formatter'; +import { useEditor } from '@/hooks/useEditor'; export type EditorProps = { taskId: number; @@ -48,6 +52,8 @@ const CodeEditor: React.FC = (props) => { const [isModalOpen, setIsModalOpen] = useState(false); const [diff, setDiff] = useState([]); + const { fullscreen, setFullscreen } = useEditor(); + const [editorIns, setEditorIns] = useState(null); const currentTab = getCurrentTab(panes, activeKey) as DataStudioTabsItemType; const currentData = currentTab.params.taskData; @@ -89,62 +95,105 @@ const CodeEditor: React.FC = (props) => { }; return ( - <> - - - { - editor.layout(); - editor.focus(); + +
+ + { + editor.layout(); + editor.focus(); - editor.onDidChangeCursorPosition((e) => { - props.footContainer.codePosition = [e.position.lineNumber, e.position.column]; - dispatch({ - type: STUDIO_MODEL.saveFooterValue, - payload: { ...props.footContainer } + editor.onDidChangeCursorPosition((e) => { + props.footContainer.codePosition = [e.position.lineNumber, e.position.column]; + dispatch({ + type: STUDIO_MODEL.saveFooterValue, + payload: { ...props.footContainer } + }); }); - }); - }} - onChange={(v) => { - if (!currentData || !currentTab) { - return; - } - if (typeof v === 'string') { - currentData.statement = v; - } - currentTab.isModified = true; - dispatch({ - type: STUDIO_MODEL.saveTabs, - payload: { ...props.tabs } - }); - }} - theme={'vs-dark'} - /> - + editor.addCommand(KeyMod.Alt | KeyCode.Digit3, () => { + editor?.trigger('anyString', 'editor.action.formatDocument'); + editor.setValue(format(editor.getValue())); + }); + setEditorIns(editor); + }} + onChange={(v) => { + if (!currentData || !currentTab) { + return; + } + if (typeof v === 'string') { + currentData.statement = v; + } + currentTab.isModified = true; + dispatch({ + type: STUDIO_MODEL.saveTabs, + payload: { ...props.tabs } + }); + }} + theme={convertCodeEditTheme()} + > +
+ {fullscreen ? ( +
+
+
); }; diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/KeyBoard/constant.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/KeyBoard/constant.tsx index 87e273f47c..aad7756ae9 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/KeyBoard/constant.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/KeyBoard/constant.tsx @@ -22,7 +22,7 @@ import { l } from '@/utils/intl'; export const KEY_BOARD_MIDDLE = [ { key: 'ctrls', - label: 'Ctrl + S', + label: 'Ctrl / Command + S', description: l('shortcut.key.save') }, { @@ -35,16 +35,16 @@ export const KEY_BOARD_MIDDLE = [ label: 'Alt + 3', description: l('shortcut.key.beautify') }, - { - key: 'f2', - label: 'F2', - description: l('shortcut.key.fullscreen') - }, - { - key: 'esc', - label: 'Esc', - description: l('shortcut.key.fullscreenClose') - } + // { + // key: 'f2', + // label: 'F2', + // description: l('shortcut.key.fullscreen') + // }, + // { + // key: 'esc', + // label: 'Esc', + // description: l('shortcut.key.fullscreenClose') + // } ]; export const KEY_BOARD_RIGHT_SLIDER = [ diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx index 4eb1f8f599..438cbb15fc 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx @@ -16,6 +16,7 @@ */ import ContentScroll from '@/components/Scroll/ContentScroll'; +import { useEditor } from '@/hooks/useEditor'; import useThemeValue from '@/hooks/useThemeValue'; import { STUDIO_TAG_RIGHT_CONTEXT_MENU } from '@/pages/DataStudio/constants'; import { @@ -50,6 +51,9 @@ const MiddleContainer = (props: any) => { } = props; const themeValue = useThemeValue(); + + const {fullscreen} = useEditor(); + const [contextMenuPosition, setContextMenuPosition] = useState({}); const [contextMenuVisible, setContextMenuVisible] = useState(false); const [includeTab, setIncludeTab] = useState({}); @@ -248,7 +252,7 @@ const MiddleContainer = (props: any) => { ), children: ( - + {renderContent()} ) diff --git a/dinky-web/src/pages/DataStudio/index.tsx b/dinky-web/src/pages/DataStudio/index.tsx index 4e7925afda..7612d64a29 100644 --- a/dinky-web/src/pages/DataStudio/index.tsx +++ b/dinky-web/src/pages/DataStudio/index.tsx @@ -16,6 +16,7 @@ */ import { AuthorizedObject, useAccess } from '@/hooks/useAccess'; +import { FullScreenProvider, useEditor } from '@/hooks/useEditor'; import useThemeValue from '@/hooks/useThemeValue'; import BottomContainer from '@/pages/DataStudio/BottomContainer'; import FooterContainer from '@/pages/DataStudio/FooterContainer'; @@ -68,6 +69,8 @@ const DataStudio = (props: any) => { const persist = app._store.persist; const bottomHeight = bottomContainer.selectKey === '' ? 0 : bottomContainer.height; + const { fullscreen } = useEditor(); + const getClientSize = () => ({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, @@ -197,49 +200,56 @@ const DataStudio = (props: any) => { ); return ( - - -
- - - -
- {LeftTopMenu} - {LeftBottomMenu} -
-
- - -
- - - + fullscreen ? ( + + ) : ( + + +
+ + + +
+ {LeftTopMenu} + {LeftBottomMenu} +
+
+ + +
+ + + + + +
+ {}
- -
- {} -
- - - {RightTopMenu} - - - {} -
- - + + + {RightTopMenu} + +
+ {} +
+
+
+ ) ); }; diff --git a/dinky-web/src/pages/DataStudio/route.tsx b/dinky-web/src/pages/DataStudio/route.tsx index 60e392a533..0c94b31ff6 100644 --- a/dinky-web/src/pages/DataStudio/route.tsx +++ b/dinky-web/src/pages/DataStudio/route.tsx @@ -32,6 +32,7 @@ import HistoryVersion from '@/pages/DataStudio/RightContainer/HistoryVersion'; import JobConfig from '@/pages/DataStudio/RightContainer/JobConfig'; import JobInfo from '@/pages/DataStudio/RightContainer/JobInfo'; import SavePoints from '@/pages/DataStudio/RightContainer/SavePoints'; +import { convertCodeEditTheme } from '@/utils/function'; import { l } from '@/utils/intl'; import { ApartmentOutlined, @@ -210,6 +211,7 @@ export const LeftBottomMoreTabs: { [c: string]: TabProp[] } = { } }} language={'text'} + theme={convertCodeEditTheme()} /> ) }, diff --git a/dinky-web/src/utils/function.tsx b/dinky-web/src/utils/function.tsx index a6e39aef77..180b33c47a 100644 --- a/dinky-web/src/utils/function.tsx +++ b/dinky-web/src/utils/function.tsx @@ -124,40 +124,41 @@ export function getLocalTheme(): string { * @constructor */ export function convertCodeEditTheme() { - /** - * user can define a new theme by calling the defineTheme method on the editor. - */ - editor.defineTheme(CODE_EDIT_THEME.VS_CUSTOME, { - base: 'vs', // 指定基础主题 , 可选值: 'vs', 'vs-dark', 'hc-black' , base theme - inherit: true, // 是否继承基础主题配置 , 默认为 true, is to inherit the base theme - // rules is an array of rules. The array must not be sparse (i.e. do not use holes). - rules: [ - { token: 'comment', foreground: '#008800', fontStyle: 'italic' }, - { token: 'keyword', foreground: '#064cff', fontStyle: 'bold' }, - { token: 'string', foreground: '#507dee' }, - { token: 'delimiter', foreground: '#041d81' }, - { - token: 'readonly', - foreground: '#e73a6e', - background: '#141414', - fontStyle: 'italic' - }, - { token: 'number', foreground: '#ffffff' } - ], - // colors is an object of color identifiers and their color values. - colors: { - 'editor.background': '#5d5b5b', // editor background color - 'editor.lineHighlightBackground': '#959cb6', // editor line highlight background color - 'editorLineNumber.foreground': '#ffffff', // editor line number color - 'editorCursor.foreground': '#ffffff', // editor cursor color - 'editorIndentGuide.background': '#ffffff', // editor indent guide color - 'editor.foreground': '#ffffff', // editor selection highlight border color - 'editor.selectionBackground': '#4ba1ef', // editor selection highlight color - 'editor.selectionHighlightBorder': '#4ba1ef', // editor selection highlight border color - 'editor.findMatchBackground': '#4ba1ef', // editor find match highlight color - 'editor.wordHighlightBackground': '#8bb2d2' // editor word highlight color - } - }); + + /** + * user can define a new theme by calling the defineTheme method on the editor. + */ + editor.defineTheme(CODE_EDIT_THEME.VS_CUSTOME, { + base: 'vs', // 指定基础主题 , 可选值: 'vs', 'vs-dark', 'hc-black' , base theme + inherit: true, // 是否继承基础主题配置 , 默认为 true, is to inherit the base theme + // rules is an array of rules. The array must not be sparse (i.e. do not use holes). + rules: [ + { token: 'comment', foreground: '#008800', fontStyle: 'italic' }, + { token: 'keyword', foreground: '#064cff', fontStyle: 'bold' }, + { token: 'string', foreground: '#507dee' }, + { token: 'delimiter', foreground: '#041d81' }, + { + token: 'readonly', + foreground: '#e73a6e', + background: '#141414', + fontStyle: 'italic' + }, + { token: 'number', foreground: '#ffffff' } + ], + // colors is an object of color identifiers and their color values. + colors: { + 'editor.background': '#2f2e2e', // editor background color + 'editor.lineHighlightBackground': '#959cb6', // editor line highlight background color + 'editorLineNumber.foreground': '#ffffff', // editor line number color + 'editorCursor.foreground': '#ffffff', // editor cursor color + 'editorIndentGuide.background': '#ffffff', // editor indent guide color + 'editor.foreground': '#ffffff', // editor selection highlight border color + 'editor.selectionBackground': '#4ba1ef', // editor selection highlight color + 'editor.selectionHighlightBorder': '#4ba1ef', // editor selection highlight border color + 'editor.findMatchBackground': '#4ba1ef', // editor find match highlight color + 'editor.wordHighlightBackground': '#8bb2d2' // editor word highlight color + } + }); const theme = getLocalTheme(); switch (theme) { diff --git a/script/sql/dinky-mysql.sql b/script/sql/dinky-mysql.sql index 8fb00a0ae1..3c477fb7ec 100644 --- a/script/sql/dinky-mysql.sql +++ b/script/sql/dinky-mysql.sql @@ -1047,7 +1047,7 @@ CREATE TABLE `dinky_metrics` ( `position` int(11) DEFAULT NULL COMMENT 'position', `show_type` varchar(255) DEFAULT NULL COMMENT 'show type', `show_size` varchar(255) DEFAULT NULL COMMENT 'show size', - `title` varchar(255) DEFAULT NULL COMMENT 'title', + `title` longtext DEFAULT NULL COMMENT 'title', `layout_name` varchar(255) DEFAULT NULL COMMENT 'layout name', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create time', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'update time', diff --git a/script/sql/dinky-pg.sql b/script/sql/dinky-pg.sql index f21c383f51..2f4a3ecc80 100644 --- a/script/sql/dinky-pg.sql +++ b/script/sql/dinky-pg.sql @@ -1975,7 +1975,7 @@ CREATE TABLE "public"."dinky_metrics" ( "position" int4, "show_type" varchar(255) COLLATE "pg_catalog"."default", "show_size" varchar(255) COLLATE "pg_catalog"."default", - "title" varchar(255) COLLATE "pg_catalog"."default", + "title" text COLLATE "pg_catalog"."default", "layout_name" varchar(255) COLLATE "pg_catalog"."default", "create_time" timestamp(6) NOT null, "update_time" timestamp(6) NOT null, @@ -2441,4 +2441,4 @@ INSERT INTO public.dinky_alert_template VALUES (1, 'Default', ' [Go toTask Web](http://${taskUrl}) ', 1, null, null); -COMMIT; \ No newline at end of file +COMMIT; From 2382e5c3087bcdf85b10a67665320792b98044cc Mon Sep 17 00:00:00 2001 From: Licho Date: Thu, 26 Oct 2023 09:33:18 +0800 Subject: [PATCH 2/5] refactor: stochastic modify (#2437) * refactor: stomach modify * Spotless Apply --------- Co-authored-by: leechor --- .../org/dinky/cdc/AbstractSinkBuilder.java | 51 ++++++++----------- .../org/dinky/cdc/SinkBuilderFactory.java | 13 +---- .../cdc/postgres/PostgresCDCBuilder.java | 1 + .../java/org/dinky/executor/Executor.java | 9 ++-- dinky-web/src/models/Sse.tsx | 4 +- 5 files changed, 30 insertions(+), 48 deletions(-) diff --git a/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/AbstractSinkBuilder.java b/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/AbstractSinkBuilder.java index f16a6a6205..5447260fc6 100644 --- a/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/AbstractSinkBuilder.java +++ b/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/AbstractSinkBuilder.java @@ -70,6 +70,7 @@ import java.util.Map; import java.util.Optional; import java.util.Properties; +import java.util.stream.Collectors; import javax.xml.bind.DatatypeConverter; @@ -239,13 +240,16 @@ protected List createInsertOperations( List operations = customTableEnvironment.getParser().parse(cdcSqlInsert); logger.info("Create {} FlinkSQL insert into successful...", tableName); + if (operations.isEmpty()) { + return operations; + } + try { - if (!operations.isEmpty()) { - Operation operation = operations.get(0); - if (operation instanceof ModifyOperation) { - modifyOperations.add((ModifyOperation) operation); - } + Operation operation = operations.get(0); + if (operation instanceof ModifyOperation) { + modifyOperations.add((ModifyOperation) operation); } + } catch (Exception e) { logger.error("Translate to plan occur exception: {}", e.toString()); throw e; @@ -445,53 +449,38 @@ public interface ConvertType { @Override public String getSinkSchemaName(Table table) { - String schemaName = table.getSchema(); - if (config.getSink().containsKey("sink.db")) { - schemaName = config.getSink().get("sink.db"); - } - return schemaName; + return config.getSink().getOrDefault("sink.db", table.getSchema()); } @Override public String getSinkTableName(Table table) { String tableName = table.getName(); - if (config.getSink().containsKey("table.prefix.schema") - && (Boolean.parseBoolean(config.getSink().get("table.prefix.schema")))) { + Map sink = config.getSink(); + if (Boolean.parseBoolean(sink.get("table.prefix.schema"))) { tableName = table.getSchema() + "_" + tableName; } - if (config.getSink().containsKey("table.prefix")) { - tableName = config.getSink().get("table.prefix") + tableName; - } - - if (config.getSink().containsKey("table.suffix")) { - tableName = tableName + config.getSink().get("table.suffix"); - } + tableName = sink.getOrDefault("table.prefix", "") + tableName + sink.getOrDefault("table.suffix", ""); - if (config.getSink().containsKey("table.lower") - && (Boolean.parseBoolean(config.getSink().get("table.lower")))) { + if (Boolean.parseBoolean(sink.get("table.lower"))) { tableName = tableName.toLowerCase(); } - if (config.getSink().containsKey("table.upper") - && (Boolean.parseBoolean(config.getSink().get("table.upper")))) { + if (Boolean.parseBoolean(sink.get("table.upper"))) { tableName = tableName.toUpperCase(); } return tableName; } protected List getPKList(Table table) { - List pks = new ArrayList<>(); if (Asserts.isNullCollection(table.getColumns())) { - return pks; + return new ArrayList<>(); } - for (Column column : table.getColumns()) { - if (column.isKeyFlag()) { - pks.add(column.getName()); - } - } - return pks; + return table.getColumns().stream() + .filter(Column::isKeyFlag) + .map(Column::getName) + .collect(Collectors.toList()); } protected ZoneId getSinkTimeZone() { diff --git a/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/SinkBuilderFactory.java b/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/SinkBuilderFactory.java index b5605f0d6a..0dbb97b82e 100644 --- a/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/SinkBuilderFactory.java +++ b/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/SinkBuilderFactory.java @@ -28,7 +28,6 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ServiceLoader; @@ -64,16 +63,8 @@ private static Map> getPlusSinkBuilder() { final ServiceLoader loader = ServiceLoader.load(SinkBuilder.class); final List sinkBuilders = new ArrayList<>(); - final Iterator factories = loader.iterator(); - while (factories.hasNext()) { - try { - final SinkBuilder factory = factories.next(); - if (factory != null) { - sinkBuilders.add(factory); - } - } catch (Throwable e) { - logger.warn("Could not load service provider class : {}", e.getMessage()); - } + for (SinkBuilder factory : loader) { + sinkBuilders.add(factory); } Map> plusSinkBuilder = sinkBuilders.stream() diff --git a/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/postgres/PostgresCDCBuilder.java b/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/postgres/PostgresCDCBuilder.java index c967f6c2f4..044b5b51f8 100644 --- a/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/postgres/PostgresCDCBuilder.java +++ b/dinky-cdc/dinky-cdc-core/src/main/java/org/dinky/cdc/postgres/PostgresCDCBuilder.java @@ -82,6 +82,7 @@ public DataStreamSource build(StreamExecutionEnvironment env) { } else { sourceBuilder.schemaList(); } + List schemaTableNameList = config.getSchemaTableNameList(); if (Asserts.isNotNullCollection(schemaTableNameList)) { sourceBuilder.tableList(schemaTableNameList.toArray(new String[0])); diff --git a/dinky-executor/src/main/java/org/dinky/executor/Executor.java b/dinky-executor/src/main/java/org/dinky/executor/Executor.java index fc298b7cd3..3c60259c49 100644 --- a/dinky-executor/src/main/java/org/dinky/executor/Executor.java +++ b/dinky-executor/src/main/java/org/dinky/executor/Executor.java @@ -181,12 +181,13 @@ public TableResult executeSql(String statement) { if (Asserts.isNotNull(flinkInterceptorResult.getTableResult())) { return flinkInterceptorResult.getTableResult(); } - if (!flinkInterceptorResult.isNoExecute()) { - KerberosUtil.authenticate(setConfig); - return tableEnvironment.executeSql(statement); - } else { + + if (flinkInterceptorResult.isNoExecute()) { return CustomTableResultImpl.TABLE_RESULT_OK; } + + KerberosUtil.authenticate(setConfig); + return tableEnvironment.executeSql(statement); } public void initUDF(String... udfFilePath) { diff --git a/dinky-web/src/models/Sse.tsx b/dinky-web/src/models/Sse.tsx index ede6f726b1..e15d26823e 100644 --- a/dinky-web/src/models/Sse.tsx +++ b/dinky-web/src/models/Sse.tsx @@ -39,7 +39,7 @@ export default () => { const topics: string[] = []; subscriberRef.current.forEach((sub) => topics.push(...sub.topic)); const para = { sessionKey: uuidRef.current, topics: topics }; - await postAll('api/sse/subscribeTopic', para).catch(e=>ErrorMessage(e)) + await postAll('api/sse/subscribeTopic', para).catch((e) => ErrorMessage(e)); }; const reconnectSse = () => { @@ -54,7 +54,7 @@ export default () => { useEffect(() => { if (eventSource) { - eventSource.onopen = () => setTimeout(()=>subscribe(),1000); + eventSource.onopen = () => setTimeout(() => subscribe(), 1000); eventSource.onmessage = (e) => { try { const data: SseData = JSON.parse(e.data); From 7bb0aaefba9a3afde6e8ef7b835a04ad915dbcf9 Mon Sep 17 00:00:00 2001 From: yqwoe Date: Thu, 26 Oct 2023 15:25:31 +0800 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=E6=95=B0=E6=8D=AE=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=99=A8=E5=B1=8F=E5=B9=95=E9=80=82=E9=85=8D?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20(#2440)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: use-service hooks * fix: 快捷键问题;编辑器主题切换问题;编辑 器全屏;数据库字段长度; * fix:提交 .mvn * fix:提交 .idea * fix:提交 .gitignore * fix:提交 maven-wrapper.jar * fix:提交 .gitignore * fix:编辑器屏幕适配问题 * fix:编辑器屏幕适配问题 * fix:控制台树形折叠/展开逻辑 --- .../Console/ConsoleContent.tsx | 29 +++++++++++++++++-- .../MiddleContainer/Editor/index.tsx | 10 ++++--- .../DataStudio/MiddleContainer/index.tsx | 26 ++++++++++++++--- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/dinky-web/src/pages/DataStudio/BottomContainer/Console/ConsoleContent.tsx b/dinky-web/src/pages/DataStudio/BottomContainer/Console/ConsoleContent.tsx index 0ef4acf6f6..67a5678f4f 100644 --- a/dinky-web/src/pages/DataStudio/BottomContainer/Console/ConsoleContent.tsx +++ b/dinky-web/src/pages/DataStudio/BottomContainer/Console/ConsoleContent.tsx @@ -44,12 +44,26 @@ export interface ProcessStep extends DataNode { time: number; log: string; lastUpdateStep: ProcessStep; + children: ProcessStep[]; } + +const buildExpandKeys = (node: ProcessStep) => { + const keys: Key[] = []; + keys.push(node.key); + if (node.children.length > 0) { + node.children.forEach((item: ProcessStep) => { + keys.push(...buildExpandKeys(item)); + }); + } + return keys; +}; + const ConsoleContent = (props: ConsoleProps) => { const { tab } = props; const [selectNode, setSelectNode] = useState(); const [processNode, setProcessNode] = useState(); + const [expandedKeys, setExpandedKeys] = useState([]); const process = `FlinkSubmit/${tab.params.taskId}`; const topic = `${SSE_TOPIC.PROCESS_CONSOLE}/${process}`; @@ -105,6 +119,16 @@ const ConsoleContent = (props: ConsoleProps) => { ); }; + useEffect(() => { + if (processNode) { + setExpandedKeys(buildExpandKeys(processNode)); + } + }, [processNode]); + + const handleExpand = (expandedKeys: Key[]) => { + setExpandedKeys(expandedKeys); + }; + return (
{ titleRender={renderTitle} onSelect={onSelect} treeData={[processNode]} - expandAction={'doubleClick'} - defaultExpandParent - defaultExpandAll + expandedKeys={expandedKeys} + onExpand={handleExpand} /> ) : ( diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx index 12664ae814..6bbb0af488 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx @@ -41,13 +41,15 @@ import { useEditor } from '@/hooks/useEditor'; export type EditorProps = { taskId: number; + height?: number; }; const CodeEditor: React.FC = (props) => { const { taskId, tabs: { panes, activeKey }, - dispatch + dispatch, + height, } = props; const [isModalOpen, setIsModalOpen] = useState(false); @@ -96,7 +98,7 @@ const CodeEditor: React.FC = (props) => { return ( -
+
= (props) => {
{fullscreen ? ( diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx index 438cbb15fc..10529f9472 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx @@ -51,8 +51,7 @@ const MiddleContainer = (props: any) => { } = props; const themeValue = useThemeValue(); - - const {fullscreen} = useEditor(); + const { fullscreen } = useEditor(); const [contextMenuPosition, setContextMenuPosition] = useState({}); const [contextMenuVisible, setContextMenuVisible] = useState(false); @@ -224,7 +223,18 @@ const MiddleContainer = (props: any) => { } const v = item.params; - return ; + return ( + + ); } if (isMetadataTabsItemType(item)) { @@ -252,7 +262,15 @@ const MiddleContainer = (props: any) => { ), children: ( - + {renderContent()} ) From ffadb0f32d9a22f5624da5be495ccaedc1fe6212 Mon Sep 17 00:00:00 2001 From: aiwenmo <32723967+aiwenmo@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:29:48 +0800 Subject: [PATCH 4/5] [Feature-2438][studio] Add Flink SQL debug (#2439) Co-authored-by: wenmo <32723967+wenmo@users.noreply.github.com> --- .../org/dinky/controller/TaskController.java | 15 ++++ .../java/org/dinky/data/dto/DebugDTO.java | 69 +++++++++++++++++++ .../org/dinky/data/enums/BusinessType.java | 3 + .../java/org/dinky/service/TaskService.java | 10 +++ .../dinky/service/impl/TaskServiceImpl.java | 34 ++++++++- .../org/dinky/data/enums/ProcessStepType.java | 1 + .../java/org/dinky/data/enums/Status.java | 3 +- .../resources/i18n/messages_en_US.properties | 2 + .../resources/i18n/messages_zh_CN.properties | 2 + .../data/result/AbstractResultBuilder.java | 24 +++++++ .../org/dinky/data/result/ResultBuilder.java | 9 ++- .../org/dinky/data/result/ResultRunnable.java | 19 +++-- .../data/result/SelectResultBuilder.java | 9 ++- .../src/main/java/org/dinky/job/Job.java | 7 +- .../main/java/org/dinky/job/JobManager.java | 16 ++--- dinky-web/src/locales/en-US/pages.ts | 6 +- dinky-web/src/locales/zh-CN/pages.ts | 4 ++ .../BottomContainer/Result/index.tsx | 1 + .../DataStudio/HeaderContainer/index.tsx | 38 +++++++++- .../DataStudio/HeaderContainer/service.tsx | 4 ++ 20 files changed, 246 insertions(+), 30 deletions(-) create mode 100644 dinky-admin/src/main/java/org/dinky/data/dto/DebugDTO.java create mode 100644 dinky-core/src/main/java/org/dinky/data/result/AbstractResultBuilder.java diff --git a/dinky-admin/src/main/java/org/dinky/controller/TaskController.java b/dinky-admin/src/main/java/org/dinky/controller/TaskController.java index a5772bd9df..ef2e96e387 100644 --- a/dinky-admin/src/main/java/org/dinky/controller/TaskController.java +++ b/dinky-admin/src/main/java/org/dinky/controller/TaskController.java @@ -22,6 +22,7 @@ import org.dinky.data.annotation.Log; import org.dinky.data.annotations.ExecuteProcess; import org.dinky.data.annotations.ProcessId; +import org.dinky.data.dto.DebugDTO; import org.dinky.data.dto.TaskDTO; import org.dinky.data.dto.TaskRollbackVersionDTO; import org.dinky.data.enums.BusinessType; @@ -81,6 +82,20 @@ public Result submitTask(@ProcessId @RequestParam Integer id) throws } } + @PostMapping("/debugTask") + @ApiOperation("Debug Task") + @Log(title = "Debug Task", businessType = BusinessType.DEBUG) + @ApiImplicitParam( + name = "debugTask", + value = "Debug Task", + required = true, + dataType = "DebugDTO", + paramType = "body") + public Result debugTask(@RequestBody DebugDTO debugDTO) throws Exception { + JobResult result = taskService.debugTask(debugDTO); + return Result.succeed(result, Status.EXECUTE_SUCCESS); + } + @GetMapping("/cancel") @Log(title = "Cancel Flink Job", businessType = BusinessType.TRIGGER) @ApiOperation("Cancel Flink Job") diff --git a/dinky-admin/src/main/java/org/dinky/data/dto/DebugDTO.java b/dinky-admin/src/main/java/org/dinky/data/dto/DebugDTO.java new file mode 100644 index 0000000000..fe6ff904ca --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/data/dto/DebugDTO.java @@ -0,0 +1,69 @@ +/* + * + * 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. + * + */ + +package org.dinky.data.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * Param for debug flink sql and common sql + */ +@Getter +@Setter +@ApiModel(value = "DebugDTO", description = "Param for debug flink sql and common sql") +public class DebugDTO { + + @ApiModelProperty( + value = "Task ID", + dataType = "Integer", + example = "1", + notes = "The ID of Task which is debugged") + private Integer id; + + @ApiModelProperty( + value = "Use Result", + dataType = "boolean", + example = "true", + notes = "Flag indicating whether to preview table result") + private boolean useResult = true; + + @ApiModelProperty( + value = "Use ChangeLog", + dataType = "boolean", + example = "false", + notes = "Flag indicating whether to preview change log") + private boolean useChangeLog = false; + + @ApiModelProperty( + value = "Use Auto Cancel", + dataType = "boolean", + example = "true", + notes = "Flag indicating whether to auto cancel after preview the maximum rows") + private boolean useAutoCancel = true; + + @ApiModelProperty( + value = "Max Row Number", + dataType = "Integer", + example = "1000", + notes = "The maximum number of rows to preview") + private Integer maxRowNum = 1000; +} diff --git a/dinky-admin/src/main/java/org/dinky/data/enums/BusinessType.java b/dinky-admin/src/main/java/org/dinky/data/enums/BusinessType.java index 4b4ceda736..1b09f8f933 100644 --- a/dinky-admin/src/main/java/org/dinky/data/enums/BusinessType.java +++ b/dinky-admin/src/main/java/org/dinky/data/enums/BusinessType.java @@ -36,6 +36,9 @@ public enum BusinessType { /** 提交 */ SUBMIT, + /** Debug */ + DEBUG, + /** 执行 */ EXECUTE, diff --git a/dinky-admin/src/main/java/org/dinky/service/TaskService.java b/dinky-admin/src/main/java/org/dinky/service/TaskService.java index 248a673e1f..21a2a60247 100644 --- a/dinky-admin/src/main/java/org/dinky/service/TaskService.java +++ b/dinky-admin/src/main/java/org/dinky/service/TaskService.java @@ -20,6 +20,7 @@ package org.dinky.service; import org.dinky.data.dto.AbstractStatementDTO; +import org.dinky.data.dto.DebugDTO; import org.dinky.data.dto.TaskDTO; import org.dinky.data.dto.TaskRollbackVersionDTO; import org.dinky.data.enums.JobLifeCycle; @@ -69,6 +70,15 @@ public interface TaskService extends ISuperService { */ JobResult submitTask(Integer id, String savePointPath) throws Exception; + /** + * Debug the given task and return the job result. + * + * @param debugDTO The param of preview task. + * @return A {@link JobResult} object representing the result of the submitted task. + * @throws ExcuteException If there is an error debugging the task. + */ + JobResult debugTask(DebugDTO debugDTO) throws Exception; + /** * Restart the given task and return the job result. * diff --git a/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java index e7e992cf11..81bbf84b34 100644 --- a/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java +++ b/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java @@ -25,6 +25,7 @@ import org.dinky.data.annotations.ProcessStep; import org.dinky.data.constant.CommonConstant; import org.dinky.data.dto.AbstractStatementDTO; +import org.dinky.data.dto.DebugDTO; import org.dinky.data.dto.TaskDTO; import org.dinky.data.dto.TaskRollbackVersionDTO; import org.dinky.data.enums.JobLifeCycle; @@ -196,8 +197,7 @@ public void preCheckTask(TaskDTO task) throws TaskNotDoneException, SqlExplainEx @ProcessStep(type = ProcessStepType.SUBMIT_EXECUTE) public JobResult executeJob(TaskDTO task) throws Exception { - JobResult jobResult; - jobResult = BaseTask.getTask(task).execute(); + JobResult jobResult = BaseTask.getTask(task).execute(); log.info("execute job finished,status is {}", jobResult.getStatus()); return jobResult; } @@ -238,7 +238,7 @@ public String buildEnvSql(AbstractStatementDTO task) { task.setVariables(fragmentVariableService.listEnabledVariables()); } int envId = Optional.ofNullable(task.getEnvId()).orElse(-1); - if (envId >= 0) { + if (envId > 0) { TaskDTO envTask = this.getTaskInfoById(task.getEnvId()); if (Asserts.isNotNull(envTask) && Asserts.isNotNullString(envTask.getStatement())) { sql += envTask.getStatement() + CommonConstant.LineSep; @@ -279,6 +279,34 @@ public JobResult submitTask(Integer id, String savePointPath) throws Exception { return jobResult; } + @Override + @ProcessStep(type = ProcessStepType.SUBMIT_TASK) + public JobResult debugTask(DebugDTO debugDTO) throws Exception { + initTenantByTaskId(debugDTO.getId()); + + TaskDTO taskDTO = this.getTaskInfoById(debugDTO.getId()); + taskDTO.setUseResult(debugDTO.isUseResult()); + taskDTO.setUseChangeLog(debugDTO.isUseChangeLog()); + taskDTO.setUseAutoCancel(debugDTO.isUseAutoCancel()); + taskDTO.setMaxRowNum(debugDTO.getMaxRowNum()); + // 注解自调用会失效,这里通过获取对象方法绕过此限制 + TaskServiceImpl taskServiceBean = applicationContext.getBean(TaskServiceImpl.class); + taskServiceBean.preCheckTask(taskDTO); + + JobResult jobResult = taskServiceBean.executeJob(taskDTO); + + if (Job.JobStatus.SUCCESS == jobResult.getStatus()) { + log.info("Job debug success"); + Task task = new Task(debugDTO.getId(), jobResult.getJobInstanceId()); + if (!this.updateById(task)) { + throw new BusException(Status.TASK_UPDATE_FAILED.getMessage()); + } + } else { + log.error("Job debug failed, error: " + jobResult.getError()); + } + return jobResult; + } + @Override public JobResult restartTask(Integer id, String savePointPath) throws Exception { TaskDTO task = this.getTaskInfoById(id); diff --git a/dinky-common/src/main/java/org/dinky/data/enums/ProcessStepType.java b/dinky-common/src/main/java/org/dinky/data/enums/ProcessStepType.java index 1738ebca78..d1f0ab0b21 100644 --- a/dinky-common/src/main/java/org/dinky/data/enums/ProcessStepType.java +++ b/dinky-common/src/main/java/org/dinky/data/enums/ProcessStepType.java @@ -26,6 +26,7 @@ @Getter public enum ProcessStepType { SUBMIT_TASK("SUBMIT_TASK", Status.PROCESS_SUBMIT_SUBMITTASK), + SUBMIT_DEBUG("DEBUG_TASK", Status.PROCESS_SUBMIT_SUBMITTASK), SUBMIT_PRECHECK("SUBMIT_PRECHECK", Status.PROCESS_SUBMIT_CHECKSQL), SUBMIT_EXECUTE("SUBMIT_EXECUTE", Status.PROCESS_SUBMIT_EXECUTE), SUBMIT_BUILD_CONFIG("SUBMIT_BUILD_CONFIG", Status.PROCESS_SUBMIT_BUILDCONFIG), diff --git a/dinky-common/src/main/java/org/dinky/data/enums/Status.java b/dinky-common/src/main/java/org/dinky/data/enums/Status.java index dcc96bebe5..58a5d6612e 100644 --- a/dinky-common/src/main/java/org/dinky/data/enums/Status.java +++ b/dinky-common/src/main/java/org/dinky/data/enums/Status.java @@ -85,7 +85,8 @@ public enum Status { MOVE_FAILED(9028, "move.failed"), TEST_CONNECTION_SUCCESS(9029, "test.connection.success"), TEST_CONNECTION_FAILED(9030, "test.connection.failed"), - + DEBUG_SUCCESS(9031, "debug.success"), + DEBUG_FAILED(9032, "debug.failed"), /** * user,tenant,role */ diff --git a/dinky-common/src/main/resources/i18n/messages_en_US.properties b/dinky-common/src/main/resources/i18n/messages_en_US.properties index 670d35ebac..f8be5643b1 100644 --- a/dinky-common/src/main/resources/i18n/messages_en_US.properties +++ b/dinky-common/src/main/resources/i18n/messages_en_US.properties @@ -49,6 +49,8 @@ delete.failed=Delete Failed role.binding.user=Role Already Binding User , Can Not Delete not.token=Can Not Read Token execute.success=Execute Successfully +debug.success=Debug Successfully +debug.failed=Debug Failed token.freezed=token has been frozen menu.has.assign=Menu Has Assign , Can Not Delete datasource.status.refresh.success=DataSource Status Refresh Success diff --git a/dinky-common/src/main/resources/i18n/messages_zh_CN.properties b/dinky-common/src/main/resources/i18n/messages_zh_CN.properties index a747e28415..262c2afff7 100644 --- a/dinky-common/src/main/resources/i18n/messages_zh_CN.properties +++ b/dinky-common/src/main/resources/i18n/messages_zh_CN.properties @@ -49,6 +49,8 @@ delete.failed=删除失败 role.binding.user=该角色已绑定用户,无法删除 not.token=未能读取到有效 Token execute.success=执行成功 +debug.success=调试成功 +debug.failed=调试失败 token.freezed=token 已被冻结 menu.has.assign=菜单已分配,不允许删除 datasource.status.refresh.success=数据源状态刷新成功 diff --git a/dinky-core/src/main/java/org/dinky/data/result/AbstractResultBuilder.java b/dinky-core/src/main/java/org/dinky/data/result/AbstractResultBuilder.java new file mode 100644 index 0000000000..4d940c0c9a --- /dev/null +++ b/dinky-core/src/main/java/org/dinky/data/result/AbstractResultBuilder.java @@ -0,0 +1,24 @@ +/* + * + * 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. + * + */ + +package org.dinky.data.result; + +public abstract class AbstractResultBuilder { + protected String id; +} diff --git a/dinky-core/src/main/java/org/dinky/data/result/ResultBuilder.java b/dinky-core/src/main/java/org/dinky/data/result/ResultBuilder.java index 73fbbe647b..6178fa6be3 100644 --- a/dinky-core/src/main/java/org/dinky/data/result/ResultBuilder.java +++ b/dinky-core/src/main/java/org/dinky/data/result/ResultBuilder.java @@ -31,10 +31,15 @@ public interface ResultBuilder { static ResultBuilder build( - SqlType operationType, Integer maxRowNum, boolean isChangeLog, boolean isAutoCancel, String timeZone) { + SqlType operationType, + String id, + Integer maxRowNum, + boolean isChangeLog, + boolean isAutoCancel, + String timeZone) { switch (operationType) { case SELECT: - return new SelectResultBuilder(maxRowNum, isChangeLog, isAutoCancel, timeZone); + return new SelectResultBuilder(id, maxRowNum, isChangeLog, isAutoCancel, timeZone); case SHOW: case DESC: case DESCRIBE: diff --git a/dinky-core/src/main/java/org/dinky/data/result/ResultRunnable.java b/dinky-core/src/main/java/org/dinky/data/result/ResultRunnable.java index b6d6da2bed..7b2b2c2417 100644 --- a/dinky-core/src/main/java/org/dinky/data/result/ResultRunnable.java +++ b/dinky-core/src/main/java/org/dinky/data/result/ResultRunnable.java @@ -49,14 +49,21 @@ public class ResultRunnable implements Runnable { private static final String nullColumn = ""; private final TableResult tableResult; + private final String id; private final Integer maxRowNum; private final boolean isChangeLog; private final boolean isAutoCancel; private final String timeZone; public ResultRunnable( - TableResult tableResult, Integer maxRowNum, boolean isChangeLog, boolean isAutoCancel, String timeZone) { + TableResult tableResult, + String id, + Integer maxRowNum, + boolean isChangeLog, + boolean isAutoCancel, + String timeZone) { this.tableResult = tableResult; + this.id = id; this.maxRowNum = maxRowNum; this.isChangeLog = isChangeLog; this.isAutoCancel = isAutoCancel; @@ -67,16 +74,14 @@ public ResultRunnable( public void run() { try { tableResult.getJobClient().ifPresent(jobClient -> { - String jobId = jobClient.getJobID().toHexString(); - if (!ResultPool.containsKey(jobId)) { - ResultPool.put(new SelectResult(jobId, new ArrayList<>(), new LinkedHashSet<>())); + if (!ResultPool.containsKey(id)) { + ResultPool.put(new SelectResult(id, new ArrayList<>(), new LinkedHashSet<>())); } - try { if (isChangeLog) { - catchChangLog(ResultPool.get(jobId)); + catchChangLog(ResultPool.get(id)); } else { - catchData(ResultPool.get(jobId)); + catchData(ResultPool.get(id)); } } catch (Exception e) { log.error(String.format(e.toString())); diff --git a/dinky-core/src/main/java/org/dinky/data/result/SelectResultBuilder.java b/dinky-core/src/main/java/org/dinky/data/result/SelectResultBuilder.java index e121e5f06f..f7e3236375 100644 --- a/dinky-core/src/main/java/org/dinky/data/result/SelectResultBuilder.java +++ b/dinky-core/src/main/java/org/dinky/data/result/SelectResultBuilder.java @@ -28,14 +28,16 @@ * * @since 2021/5/25 16:03 */ -public class SelectResultBuilder implements ResultBuilder { +public class SelectResultBuilder extends AbstractResultBuilder implements ResultBuilder { private final Integer maxRowNum; private final boolean isChangeLog; private final boolean isAutoCancel; private final String timeZone; - public SelectResultBuilder(Integer maxRowNum, boolean isChangeLog, boolean isAutoCancel, String timeZone) { + public SelectResultBuilder( + String id, Integer maxRowNum, boolean isChangeLog, boolean isAutoCancel, String timeZone) { + this.id = id; this.maxRowNum = Asserts.isNotNull(maxRowNum) ? maxRowNum : 100; this.isChangeLog = isChangeLog; this.isAutoCancel = isAutoCancel; @@ -46,7 +48,8 @@ public SelectResultBuilder(Integer maxRowNum, boolean isChangeLog, boolean isAut public IResult getResult(TableResult tableResult) { if (tableResult.getJobClient().isPresent()) { String jobId = tableResult.getJobClient().get().getJobID().toHexString(); - ResultRunnable runnable = new ResultRunnable(tableResult, maxRowNum, isChangeLog, isAutoCancel, timeZone); + ResultRunnable runnable = + new ResultRunnable(tableResult, id, maxRowNum, isChangeLog, isAutoCancel, timeZone); Thread thread = new Thread(runnable, jobId); thread.start(); return SelectResult.buildSuccess(jobId); diff --git a/dinky-core/src/main/java/org/dinky/job/Job.java b/dinky-core/src/main/java/org/dinky/job/Job.java index 5bbae5cdbb..9c97157af3 100644 --- a/dinky-core/src/main/java/org/dinky/job/Job.java +++ b/dinky-core/src/main/java/org/dinky/job/Job.java @@ -88,7 +88,12 @@ public static Job init( Executor executor, String statement, boolean useGateway) { - return new Job(jobConfig, type, JobStatus.INITIALIZE, statement, executorConfig, executor, useGateway); + Job job = new Job(jobConfig, type, JobStatus.INITIALIZE, statement, executorConfig, executor, useGateway); + if (!useGateway) { + job.setJobManagerAddress(executorConfig.getJobManagerAddress()); + } + JobContextHolder.setJob(job); + return job; } public JobResult getJobResult() { diff --git a/dinky-core/src/main/java/org/dinky/job/JobManager.java b/dinky-core/src/main/java/org/dinky/job/JobManager.java index ef72e208c7..94fdddfd00 100644 --- a/dinky-core/src/main/java/org/dinky/job/JobManager.java +++ b/dinky-core/src/main/java/org/dinky/job/JobManager.java @@ -340,10 +340,6 @@ public StreamGraph getJarStreamGraph(String statement) throws Exception { @ProcessStep(type = ProcessStepType.SUBMIT_EXECUTE) public JobResult executeJarSql(String statement) throws Exception { Job job = Job.init(runMode, config, executorConfig, executor, statement, useGateway); - if (!useGateway) { - job.setJobManagerAddress(executorConfig.getJobManagerAddress()); - } - JobContextHolder.setJob(job); StreamGraph streamGraph = getJarStreamGraph(statement); try { if (!useGateway) { @@ -392,10 +388,6 @@ public JobResult executeJarSql(String statement) throws Exception { @ProcessStep(type = ProcessStepType.SUBMIT_EXECUTE) public JobResult executeSql(String statement) throws Exception { Job job = Job.init(runMode, config, executorConfig, executor, statement, useGateway); - if (!useGateway) { - job.setJobManagerAddress(executorConfig.getJobManagerAddress()); - } - JobContextHolder.setJob(job); ready(); String currentSql = ""; DinkyClassLoaderUtil.initClassLoader(config); @@ -462,6 +454,7 @@ public JobResult executeSql(String statement) throws Exception { // Build insert result. IResult result = ResultBuilder.build( SqlType.INSERT, + job.getId().toString(), config.getMaxRowNum(), config.isUseChangeLog(), config.isUseAutoCancel(), @@ -498,6 +491,7 @@ public JobResult executeSql(String statement) throws Exception { if (config.isUseResult()) { IResult result = ResultBuilder.build( item.getType(), + job.getId().toString(), config.getMaxRowNum(), config.isUseChangeLog(), config.isUseAutoCancel(), @@ -524,6 +518,7 @@ public JobResult executeSql(String statement) throws Exception { if (config.isUseResult()) { IResult result = ResultBuilder.build( item.getType(), + job.getId().toString(), config.getMaxRowNum(), config.isUseChangeLog(), config.isUseAutoCancel(), @@ -591,6 +586,7 @@ public JobResult executeSql(String statement) throws Exception { if (config.isUseResult()) { IResult result = ResultBuilder.build( SqlType.EXECUTE, + job.getId().toString(), config.getMaxRowNum(), config.isUseChangeLog(), config.isUseAutoCancel(), @@ -676,7 +672,8 @@ public IResult executeDDL(String statement) { } LocalDateTime startTime = LocalDateTime.now(); TableResult tableResult = executor.executeSql(newStatement); - result = ResultBuilder.build(operationType, config.getMaxRowNum(), false, false, executor.getTimeZone()) + result = ResultBuilder.build( + operationType, null, config.getMaxRowNum(), false, false, executor.getTimeZone()) .getResult(tableResult); result.setStartTime(startTime); } @@ -757,7 +754,6 @@ public static GatewayResult deploySessionCluster(GatewayConfig gatewayConfig) { public JobResult executeJar() { // TODO 改为ProcessStep注释 Job job = Job.init(runMode, config, executorConfig, executor, null, useGateway); - JobContextHolder.setJob(job); ready(); try { GatewayResult gatewayResult = diff --git a/dinky-web/src/locales/en-US/pages.ts b/dinky-web/src/locales/en-US/pages.ts index 8f9fc4b0a6..b2c01e1a78 100644 --- a/dinky-web/src/locales/en-US/pages.ts +++ b/dinky-web/src/locales/en-US/pages.ts @@ -298,9 +298,12 @@ export default { * */ 'pages.datastudio.editor.check': 'Check current FlinkSql', + 'pages.datastudio.editor.debug': 'Debug', 'pages.datastudio.editor.exec': 'Execute', - 'pages.datastudio.editor.exec.error': 'Task [jobName}] execution failed', + 'pages.datastudio.editor.exec.error': 'Task [{jobName}] execution failed', + 'pages.datastudio.editor.debug.error': 'Task [{jobName}] debug failed', 'pages.datastudio.editor.exec.success': 'Execution succeeded', + 'pages.datastudio.editor.debug.success': 'Debug succeeded', 'pages.datastudio.editor.execute.warn': 'The execution mode of this task is [{type}], which does not support SQL query, please save it manually and use the button on the right - job submission', 'pages.datastudio.editor.explan': 'Get the current FlinkSql execution graph', @@ -309,6 +312,7 @@ export default { 'pages.datastudio.editor.stop.job': 'Stop job', 'pages.datastudio.editor.stop.jobConfirm': 'Are you sure to stop the job [{jobName}]? ', 'pages.datastudio.editor.submitting': 'The new task [{jobName}] is executing', + 'pages.datastudio.editor.debugging': 'The new task [{jobName}] is debugging', 'pages.datastudio.editor.onlyread': 'Task has been published, modification is prohibited, please go offline first', 'pages.datastudio.editor.notsave': 'Current changes are not saved! ', diff --git a/dinky-web/src/locales/zh-CN/pages.ts b/dinky-web/src/locales/zh-CN/pages.ts index cac67bda53..b48347c1ea 100644 --- a/dinky-web/src/locales/zh-CN/pages.ts +++ b/dinky-web/src/locales/zh-CN/pages.ts @@ -291,8 +291,11 @@ export default { * */ 'pages.datastudio.editor.check': '检查', + 'pages.datastudio.editor.debug': '调试', 'pages.datastudio.editor.exec': '运行', 'pages.datastudio.editor.exec.error': '任务【{jobName}】执行失败', + 'pages.datastudio.editor.debug.error': '任务【{jobName}】调试失败', + 'pages.datastudio.editor.debug.success': '调试成功', 'pages.datastudio.editor.exec.success': '执行成功', 'pages.datastudio.editor.execute.warn': '该任务执行模式为【{type}】,不支持 SQL 查询,请手动保存后使用右侧按钮——作业提交', @@ -302,6 +305,7 @@ export default { 'pages.datastudio.editor.stop.job': '停止作业', 'pages.datastudio.editor.stop.jobConfirm': '确定停止作业【{jobName}】吗?', 'pages.datastudio.editor.submitting': '新任务【{jobName}】正在执行', + 'pages.datastudio.editor.debugging': '新任务【{jobName}】正在调试', 'pages.datastudio.editor.onlyread': '任务已发布,禁止修改,请先下线任务', 'pages.datastudio.editor.notsave': '当前修改内容未保存!', 'pages.datastudio.editor.notsave.note': '继续将抛弃所修改内容,确定继续吗?', diff --git a/dinky-web/src/pages/DataStudio/BottomContainer/Result/index.tsx b/dinky-web/src/pages/DataStudio/BottomContainer/Result/index.tsx index 02069c6e84..0217194e03 100644 --- a/dinky-web/src/pages/DataStudio/BottomContainer/Result/index.tsx +++ b/dinky-web/src/pages/DataStudio/BottomContainer/Result/index.tsx @@ -141,6 +141,7 @@ const Result = (props: any) => { } } else { // flink sql + // to do: get job data by history id list, not flink jid if (current.jobInstanceId) { const res = await postAll(API_CONSTANTS.GET_JOB_BY_ID, { id: current.jobInstanceId diff --git a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx index 33887af2b1..85aec32d86 100644 --- a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx @@ -28,7 +28,7 @@ import { projectCommonShow } from '@/pages/DataStudio/HeaderContainer/function'; import { - cancelTask, + cancelTask, debugTask, executeSql, getJobPlan, onLineTask @@ -44,6 +44,7 @@ import { connect } from '@@/exports'; import { ApartmentOutlined, CaretRightFilled, + BugOutlined, EnvironmentOutlined, FundOutlined, MergeCellsOutlined, @@ -133,6 +134,26 @@ const HeaderContainer = (props: any) => { }); }; + const handlerDebug = async () => { + if (!currentData) return; + + const res = await debugTask( + l('pages.datastudio.editor.debugging', '', { jobName: currentData.name }), + currentData + ); + + if (!res) return; + updateJobRunningMsg({ + taskId: currentData.id, + jobName: currentData.name, + jobState: res.datas.status, + runningLog: res.msg + }); + messageApi.success(l('pages.datastudio.editor.debug.success')); + currentData.status = JOB_STATUS.RUNNING; + saveTabs({ ...props.tabs }); + }; + const handlerSubmit = async () => { if (!currentData) return; const saved = currentData.step == JOB_LIFE_CYCLE.ONLINE ? true : await handleSave(); @@ -148,7 +169,7 @@ const HeaderContainer = (props: any) => { taskId: currentData.id, jobName: currentData.name, jobState: res.datas.status, - runningLog: res.msg + runningLog: res.msg, }); messageApi.success(l('pages.datastudio.editor.exec.success')); currentData.status = JOB_STATUS.RUNNING; @@ -262,6 +283,19 @@ const HeaderContainer = (props: any) => { type: 'primary' } }, + { + // Debug button + icon: , + title: l('pages.datastudio.editor.debug'), + click: handlerDebug, + hotKey: (e: KeyboardEvent) => e.shiftKey && e.key === 'F9', + hotKeyDesc: 'Shift+F9', + isShow: currentTab?.type == TabsPageType.project && !isRunning(currentData), + props: { + style: { background: '#52c41a' }, + type: 'primary' + } + }, { // 停止按钮 icon: , diff --git a/dinky-web/src/pages/DataStudio/HeaderContainer/service.tsx b/dinky-web/src/pages/DataStudio/HeaderContainer/service.tsx index 90259c0026..2180b2ba65 100644 --- a/dinky-web/src/pages/DataStudio/HeaderContainer/service.tsx +++ b/dinky-web/src/pages/DataStudio/HeaderContainer/service.tsx @@ -29,6 +29,10 @@ export async function getJobPlan(title: string, params: any) { return handleOption('/api/task/getJobPlan', title, params); } +export async function debugTask(title: string, params: any) { + return postAll('/api/task/debugTask', params); +} + export async function executeSql(title: string, id: number) { return handleGetOption('/api/task/submitTask', title, { id }); } From 2bcc490dde4c8da7aafbf6a1080f919a8a4cdef6 Mon Sep 17 00:00:00 2001 From: zhu-mingye <934230207@qq.com> Date: Thu, 26 Oct 2023 03:32:58 -0500 Subject: [PATCH 5/5] refactor extra global variables (#2441) * Spotless Apply * Spotless Apply * refactor extra global variables * refactor extra global variables * Spotless Apply --------- Co-authored-by: zhu-mingye --- .../org/dinky/constant/FlinkSQLConstant.java | 9 -- .../org/dinky/executor/VariableManager.java | 134 +++++------------- dinky-web/src/global.less | 4 +- .../DataStudio/FooterContainer/index.tsx | 2 +- .../DataStudio/HeaderContainer/index.tsx | 5 +- .../MiddleContainer/Editor/index.tsx | 4 +- .../MiddleContainer/KeyBoard/constant.tsx | 2 +- dinky-web/src/pages/DataStudio/index.tsx | 93 ++++++------ dinky-web/src/utils/function.tsx | 69 +++++---- 9 files changed, 120 insertions(+), 202 deletions(-) diff --git a/dinky-executor/src/main/java/org/dinky/constant/FlinkSQLConstant.java b/dinky-executor/src/main/java/org/dinky/constant/FlinkSQLConstant.java index 3aea355ef1..0db0b7f5c2 100644 --- a/dinky-executor/src/main/java/org/dinky/constant/FlinkSQLConstant.java +++ b/dinky-executor/src/main/java/org/dinky/constant/FlinkSQLConstant.java @@ -36,13 +36,4 @@ public interface FlinkSQLConstant { /** The define identifier of FlinkSQL Variable */ String VARIABLES = ":="; - - /** The define identifier of FlinkSQL Date Variable */ - String INNER_DATE_KEY = "_CURRENT_DATE_"; - - /** The define identifier of FlinkSQL Timestamp Variable */ - String INNER_TIMESTAMP_KEY = "_CURRENT_TIMESTAMP_"; - - /** 内置日期变量格式 确定后不能修改 */ - String INNER_DATE_FORMAT = "yyyy-MM-dd"; } diff --git a/dinky-executor/src/main/java/org/dinky/executor/VariableManager.java b/dinky-executor/src/main/java/org/dinky/executor/VariableManager.java index f401831e68..c9c6f0c5b6 100644 --- a/dinky-executor/src/main/java/org/dinky/executor/VariableManager.java +++ b/dinky-executor/src/main/java/org/dinky/executor/VariableManager.java @@ -33,17 +33,20 @@ import org.apache.flink.types.Row; import org.apache.flink.util.StringUtils; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.extra.expression.engine.jexl.JexlEngine; + /** * Flink Sql Variable Manager * @@ -55,6 +58,22 @@ public final class VariableManager { static final String SHOW_VARIABLES = "SHOW VARIABLES"; private final Map variables; + public static final JexlEngine ENGINE = new JexlEngine(); + + /** + *

+ * engine , key is variable name , value is class . + * for example: + * random -> RandomUtil -> about random operation + * date -> DateUtil -> about date operation + * id -> IdUtil -> to generate random uuid + * ... + */ + public static final Dict ENGINE_CONTEXT = Dict.create() + .set("random", RandomUtil.class) + .set("date", DateUtil.class) + .set("id", IdUtil.class); + public VariableManager() { variables = new HashMap<>(); } @@ -127,21 +146,18 @@ public void unregisterVariable(String variableName, boolean ignoreIfNotExists) { * @throws CatalogException if the unregistration of the sql variable under the given name * failed. But at the moment, with CatalogException, not SqlException */ - public String getVariable(String variableName) { + public Object getVariable(String variableName) { checkArgument( !StringUtils.isNullOrWhitespaceOnly(variableName), "sql variableName name cannot be null or empty."); - - if (variables.containsKey(variableName)) { - return variables.get(variableName); - } - - if (isInnerDateVariable(variableName)) { - return parseDateVariable(variableName); - } else if (isInnerTimestampVariable(variableName)) { - return parseTimestampVar(variableName); + try { + if (variables.containsKey(variableName)) { + return variables.get(variableName); + } + // use jexl to parse variable value + return ENGINE.eval(variableName, ENGINE_CONTEXT); + } catch (Exception e) { + throw new CatalogException(format("The variable of sql %s does not exist.", variableName)); } - - throw new CatalogException(format("The variable of sql %s does not exist.", variableName)); } /** @@ -188,7 +204,7 @@ public Table getVariablesTable(CustomTableEnvironmentImpl environment) { } public boolean checkShowVariables(String sql) { - return SHOW_VARIABLES.equals(sql.trim().toUpperCase()); + return SHOW_VARIABLES.equalsIgnoreCase(sql.trim()); } /** @@ -229,95 +245,11 @@ private String replaceVariable(String statement) { StringBuffer sb = new StringBuffer(); while (m.find()) { String key = m.group(1); - String value = getVariable(key); + Object value = getVariable(key); m.appendReplacement(sb, ""); - - // if value is null, parse inner date variable - if (value == null) { - if (isInnerDateVariable(key)) { - value = parseDateVariable(key); - } else if (isInnerTimestampVariable(key)) { - value = parseTimestampVar(key); - } - } - sb.append(value == null ? "" : value); } m.appendTail(sb); return sb.toString(); } - - /** - * verify if key is inner variable, such as _CURRENT_DATE_ - 1 - * - * @param key - * @return - */ - private boolean isInnerDateVariable(String key) { - return key.startsWith(FlinkSQLConstant.INNER_DATE_KEY); - } - - /** - * verify if key is inner variable, such as _CURRENT_TIMESTAMP_ - 100 - * - * @param key - * @return - */ - private boolean isInnerTimestampVariable(String key) { - return key.startsWith(FlinkSQLConstant.INNER_TIMESTAMP_KEY); - } - - /** - * parse date variable - * - * @param key - * @return - */ - private String parseDateVariable(String key) { - int days = 0; - if (key.contains("+")) { - int s = key.indexOf("+") + 1; - String num = key.substring(s).trim(); - days = Integer.parseInt(num); - } else if (key.contains("-")) { - int s = key.indexOf("-") + 1; - String num = key.substring(s).trim(); - days = Integer.parseInt(num) * -1; - } - - SimpleDateFormat dtf = new SimpleDateFormat(FlinkSQLConstant.INNER_DATE_FORMAT); - Date endDate = new Date(); - Calendar calendar = Calendar.getInstance(); - calendar.setTime(endDate); - calendar.add(Calendar.DAY_OF_YEAR, days); - Date startDate = calendar.getTime(); - - return dtf.format(startDate); - } - - /** - * parse timestamp variable - * - * @param key - * @return - */ - private String parseTimestampVar(String key) { - long millisecond = 0; - try { - if (key.contains("+")) { - int s = key.indexOf("+") + 1; - String num = key.substring(s).trim(); - millisecond = Long.parseLong(num); - } else if (key.contains("-")) { - int s = key.indexOf("-") + 1; - String num = key.substring(s).trim(); - millisecond = Long.parseLong(num) * -1; - } - } catch (Exception e) { - e.printStackTrace(); - return null; - } - - return String.valueOf(System.currentTimeMillis() + millisecond); - } } diff --git a/dinky-web/src/global.less b/dinky-web/src/global.less index 894f5cc25e..0b1ba2c539 100644 --- a/dinky-web/src/global.less +++ b/dinky-web/src/global.less @@ -399,8 +399,8 @@ h5 { scrollbar-width: thin; } -.editor-full-screen{ - .ant-tabs-tabpane{ +.editor-full-screen { + .ant-tabs-tabpane { height: 100vh; } } diff --git a/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx b/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx index 8a204e6b48..e783b0086a 100644 --- a/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx @@ -20,7 +20,7 @@ import useThemeValue from '@/hooks/useThemeValue'; import JobRunningModal from '@/pages/DataStudio/FooterContainer/JobRunningModal'; import { getCurrentTab } from '@/pages/DataStudio/function'; -import { StateType,TabsPageType,VIEW} from '@/pages/DataStudio/model'; +import { StateType, TabsPageType, VIEW } from '@/pages/DataStudio/model'; import { getSseData } from '@/services/api'; import { l } from '@/utils/intl'; import { connect } from '@@/exports'; diff --git a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx index 85aec32d86..dfe2bea096 100644 --- a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx @@ -217,7 +217,7 @@ const HeaderContainer = (props: any) => { const routes: ButtonRoute[] = [ // 保存按钮 icon { - hotKey: (e: KeyboardEvent) => e.ctrlKey && e.key === 's' || e.metaKey && e.key === 's', + hotKey: (e: KeyboardEvent) => (e.ctrlKey && e.key === 's') || (e.metaKey && e.key === 's'), hotKeyDesc: 'Ctrl/Command +S', isShow: projectCommonShow(currentTab?.type), icon: , @@ -237,7 +237,8 @@ const HeaderContainer = (props: any) => { { // 检查 sql按钮 icon: , - hotKey: (e: KeyboardEvent) => e.altKey && e.code === 'Digit2' || e.altKey && e.key === '@', + hotKey: (e: KeyboardEvent) => + (e.altKey && e.code === 'Digit2') || (e.altKey && e.key === '@'), hotKeyDesc: 'Alt+2/@', title: l('pages.datastudio.editor.check'), click: () => showExplain(), diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx index 6bbb0af488..ca711272f2 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx @@ -17,6 +17,7 @@ * */ +import { useEditor } from '@/hooks/useEditor'; import { getCurrentTab } from '@/pages/DataStudio/function'; import { TASK_VAR_FILTER } from '@/pages/DataStudio/MiddleContainer/Editor/constants'; import DiffModal from '@/pages/DataStudio/MiddleContainer/Editor/DiffModal'; @@ -37,7 +38,6 @@ import { Button, Spin } from 'antd'; import { editor, KeyCode, KeyMod } from 'monaco-editor'; import React, { useState } from 'react'; import { format } from 'sql-formatter'; -import { useEditor } from '@/hooks/useEditor'; export type EditorProps = { taskId: number; @@ -49,7 +49,7 @@ const CodeEditor: React.FC = (props) => { taskId, tabs: { panes, activeKey }, dispatch, - height, + height } = props; const [isModalOpen, setIsModalOpen] = useState(false); diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/KeyBoard/constant.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/KeyBoard/constant.tsx index aad7756ae9..da4eea39cc 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/KeyBoard/constant.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/KeyBoard/constant.tsx @@ -34,7 +34,7 @@ export const KEY_BOARD_MIDDLE = [ key: 'alt3', label: 'Alt + 3', description: l('shortcut.key.beautify') - }, + } // { // key: 'f2', // label: 'F2', diff --git a/dinky-web/src/pages/DataStudio/index.tsx b/dinky-web/src/pages/DataStudio/index.tsx index 7612d64a29..3598f6d34e 100644 --- a/dinky-web/src/pages/DataStudio/index.tsx +++ b/dinky-web/src/pages/DataStudio/index.tsx @@ -16,7 +16,7 @@ */ import { AuthorizedObject, useAccess } from '@/hooks/useAccess'; -import { FullScreenProvider, useEditor } from '@/hooks/useEditor'; +import { useEditor } from '@/hooks/useEditor'; import useThemeValue from '@/hooks/useThemeValue'; import BottomContainer from '@/pages/DataStudio/BottomContainer'; import FooterContainer from '@/pages/DataStudio/FooterContainer'; @@ -199,57 +199,52 @@ const DataStudio = (props: any) => { /> ); - return ( - fullscreen ? ( - - ) : ( - - -

- - - -
- {LeftTopMenu} - {LeftBottomMenu} -
-
+ return fullscreen ? ( + + ) : ( + + +
+ + + +
+ {LeftTopMenu} + {LeftBottomMenu} +
+
- -
- - - - - -
- {} + +
+ + + + +
+ {} +
- - {RightTopMenu} - -
- {} -
-
-
- ) + + {RightTopMenu} + +
+ {} +
+ + ); }; diff --git a/dinky-web/src/utils/function.tsx b/dinky-web/src/utils/function.tsx index 180b33c47a..a7b1633eb8 100644 --- a/dinky-web/src/utils/function.tsx +++ b/dinky-web/src/utils/function.tsx @@ -124,41 +124,40 @@ export function getLocalTheme(): string { * @constructor */ export function convertCodeEditTheme() { - - /** - * user can define a new theme by calling the defineTheme method on the editor. - */ - editor.defineTheme(CODE_EDIT_THEME.VS_CUSTOME, { - base: 'vs', // 指定基础主题 , 可选值: 'vs', 'vs-dark', 'hc-black' , base theme - inherit: true, // 是否继承基础主题配置 , 默认为 true, is to inherit the base theme - // rules is an array of rules. The array must not be sparse (i.e. do not use holes). - rules: [ - { token: 'comment', foreground: '#008800', fontStyle: 'italic' }, - { token: 'keyword', foreground: '#064cff', fontStyle: 'bold' }, - { token: 'string', foreground: '#507dee' }, - { token: 'delimiter', foreground: '#041d81' }, - { - token: 'readonly', - foreground: '#e73a6e', - background: '#141414', - fontStyle: 'italic' - }, - { token: 'number', foreground: '#ffffff' } - ], - // colors is an object of color identifiers and their color values. - colors: { - 'editor.background': '#2f2e2e', // editor background color - 'editor.lineHighlightBackground': '#959cb6', // editor line highlight background color - 'editorLineNumber.foreground': '#ffffff', // editor line number color - 'editorCursor.foreground': '#ffffff', // editor cursor color - 'editorIndentGuide.background': '#ffffff', // editor indent guide color - 'editor.foreground': '#ffffff', // editor selection highlight border color - 'editor.selectionBackground': '#4ba1ef', // editor selection highlight color - 'editor.selectionHighlightBorder': '#4ba1ef', // editor selection highlight border color - 'editor.findMatchBackground': '#4ba1ef', // editor find match highlight color - 'editor.wordHighlightBackground': '#8bb2d2' // editor word highlight color - } - }); + /** + * user can define a new theme by calling the defineTheme method on the editor. + */ + editor.defineTheme(CODE_EDIT_THEME.VS_CUSTOME, { + base: 'vs', // 指定基础主题 , 可选值: 'vs', 'vs-dark', 'hc-black' , base theme + inherit: true, // 是否继承基础主题配置 , 默认为 true, is to inherit the base theme + // rules is an array of rules. The array must not be sparse (i.e. do not use holes). + rules: [ + { token: 'comment', foreground: '#008800', fontStyle: 'italic' }, + { token: 'keyword', foreground: '#064cff', fontStyle: 'bold' }, + { token: 'string', foreground: '#507dee' }, + { token: 'delimiter', foreground: '#041d81' }, + { + token: 'readonly', + foreground: '#e73a6e', + background: '#141414', + fontStyle: 'italic' + }, + { token: 'number', foreground: '#ffffff' } + ], + // colors is an object of color identifiers and their color values. + colors: { + 'editor.background': '#2f2e2e', // editor background color + 'editor.lineHighlightBackground': '#959cb6', // editor line highlight background color + 'editorLineNumber.foreground': '#ffffff', // editor line number color + 'editorCursor.foreground': '#ffffff', // editor cursor color + 'editorIndentGuide.background': '#ffffff', // editor indent guide color + 'editor.foreground': '#ffffff', // editor selection highlight border color + 'editor.selectionBackground': '#4ba1ef', // editor selection highlight color + 'editor.selectionHighlightBorder': '#4ba1ef', // editor selection highlight border color + 'editor.findMatchBackground': '#4ba1ef', // editor find match highlight color + 'editor.wordHighlightBackground': '#8bb2d2' // editor word highlight color + } + }); const theme = getLocalTheme(); switch (theme) {