This is an automated email from the ASF dual-hosted git repository.
dockerzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git
The following commit(s) were added to refs/heads/master by this push:
new 2345624b7 [INLONG-7313][Dashboard] Automatically generate a unique ID
and depth key for the menu (#7314)
2345624b7 is described below
commit 2345624b78c8bf0d5802756464f07dd3db65fe18
Author: Daniel <[email protected]>
AuthorDate: Thu Feb 2 18:57:37 2023 +0800
[INLONG-7313][Dashboard] Automatically generate a unique ID and depth key
for the menu (#7314)
---
inlong-dashboard/scripts/sync.js | 138 +++++++++++++++++++++++
inlong-dashboard/scripts/utils.js | 71 ++++++++++++
inlong-dashboard/src/components/Layout/index.tsx | 4 +-
inlong-dashboard/src/configs/locales/index.ts | 6 +-
inlong-dashboard/src/configs/menus/index.ts | 26 ++++-
inlong-dashboard/src/metas/DataWithBackend.ts | 8 ++
inlong-dashboard/src/models/index.ts | 9 +-
inlong-dashboard/src/router.tsx | 4 +-
inlong-dashboard/src/utils/index.ts | 35 ++++++
9 files changed, 285 insertions(+), 16 deletions(-)
diff --git a/inlong-dashboard/scripts/sync.js b/inlong-dashboard/scripts/sync.js
new file mode 100644
index 000000000..6d48ce25b
--- /dev/null
+++ b/inlong-dashboard/scripts/sync.js
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+const path = require('path');
+const readline = require('readline');
+const { readFile, wirteFile, getBanner } = require('./utils');
+
+const getMetaDir = (metaType, namespace, saveAs) => {
+ const dirMap = {
+ group: [
+ // from:
+ path.resolve(
+ __dirname,
+
`../../inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/group`,
+ `./${namespace.toLowerCase()}/Inlong${namespace}Request.java`,
+ ),
+ // to:
+ path.resolve(__dirname, `../src/metas/groups/${saveAs}`,
`${namespace}.ts`),
+ ],
+ consume: [
+ // from:
+ path.resolve(
+ __dirname,
+
`../../inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/consume`,
+ `./${namespace.toLowerCase()}/Consume${namespace}Request.java`,
+ ),
+ // to:
+ path.resolve(__dirname, `../src/metas/consumes/${saveAs}`,
`${namespace}.ts`),
+ ],
+ node: [
+ // from:
+ path.resolve(
+ __dirname,
+
`../../inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/node`,
+ `./es/${namespace}DataNodeRequest.java`, // TODO
+ ),
+ // to:
+ path.resolve(__dirname, `../src/metas/nodes/${saveAs}`,
`${namespace}.ts`),
+ ],
+ };
+
+ if (!dirMap[metaType]) {
+ throw new Error(`[Error] MetaType: ${metaType} not exist.`);
+ }
+
+ return dirMap[metaType];
+};
+
+const genMetaData = (metaType, namespace, list) => {
+ const MetaType = `${metaType[0].toUpperCase()}${metaType.slice(1)}`;
+ const BasicInfoName = `${MetaType}Info`;
+ const str = `\
+
+import { DataWithBackend } from '@/metas/DataWithBackend';
+import { RenderRow } from '@/metas/RenderRow';
+import { RenderList } from '@/metas/RenderList';
+import { ${BasicInfoName} } from '../common/${BasicInfoName}';
+
+const { I18n } = DataWithBackend;
+const { FieldDecorator } = RenderRow;
+
+export default class ${namespace}${MetaType}
+ extends ${BasicInfoName}
+ implements DataWithBackend, RenderRow, RenderList
+{
+ ${list
+ .map(item => {
+ const { dataType, key, defaultValue } = item;
+ return `
+ @FieldDecorator({
+ type: 'input',
+ initialValue: ${defaultValue},
+ props: {
+ },
+ })
+ @I18n('${key}')
+ ${key}: ${dataType};
+ `;
+ })
+ .join('')}
+}
+ `;
+ return str;
+};
+
+const runSync = () => {
+ const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ });
+
+ rl.question(`What's your metaType? (Support: group/consume): `, metaType => {
+ rl.question(`What's your namespace? (For example: Kafka): `, namespace => {
+ rl.question(`Save as defaults or extends? `, saveAs => {
+ rl.close();
+
+ const [backendFilePath, outputFilePath] = getMetaDir(metaType,
namespace, saveAs);
+ const dataText = readFile(backendFilePath);
+ const beginIndex = dataText.indexOf('class');
+
+ if (beginIndex !== -1) {
+ const arr = dataText.slice(beginIndex).match(/private.+;/g) || [];
+ const list = arr.map(item => {
+ if (item[item.length - 1] === ';') item = item.slice(0, -1);
+ const [, dataType, key, , defaultValue] = item.split(' ');
+ return { dataType, key, defaultValue };
+ });
+
+ const banner = getBanner();
+ const text = genMetaData(metaType, namespace, list);
+ const buffer = Buffer.from(`${banner}${text}`);
+
+ wirteFile(outputFilePath, buffer);
+ console.log(`Build ${metaType}: ${namespace} successfully! Saved at
${outputFilePath}`);
+ }
+ });
+ rl.write('defaults');
+ });
+ });
+};
+
+runSync();
diff --git a/inlong-dashboard/scripts/utils.js
b/inlong-dashboard/scripts/utils.js
new file mode 100644
index 000000000..7544bd37e
--- /dev/null
+++ b/inlong-dashboard/scripts/utils.js
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+const fs = require('fs');
+
+const readFile = path => {
+ try {
+ const result = fs.readFileSync(path);
+ return result.toString();
+ } catch (err) {
+ console.log('[FS readFile]', err);
+ return;
+ }
+};
+
+const wirteFile = (path, buffer, options) => {
+ try {
+ fs.writeFileSync(path, buffer, {
+ encoding: 'utf8',
+ flag: 'w',
+ ...options,
+ });
+ } catch (err) {
+ console.log('[FS wirteFile]', err);
+ }
+};
+
+const getBanner = () => {
+ return `
+/*
+ * 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.
+ */
+ `;
+};
+
+module.exports = {
+ readFile,
+ wirteFile,
+ getBanner,
+};
diff --git a/inlong-dashboard/src/components/Layout/index.tsx
b/inlong-dashboard/src/components/Layout/index.tsx
index eb40fdc2e..29b964616 100644
--- a/inlong-dashboard/src/components/Layout/index.tsx
+++ b/inlong-dashboard/src/components/Layout/index.tsx
@@ -18,7 +18,7 @@
*/
import { config } from '@/configs/default';
-import menus from '@/configs/menus';
+import menusTree from '@/configs/menus';
import defaultSettings from '@/defaultSettings';
import { useLocation, useSelector } from '@/hooks';
import { isDevelopEnv } from '@/utils';
@@ -49,7 +49,7 @@ const BasicLayout: React.FC = props => {
const { pathname } = location;
const roles = useSelector<State, State['roles']>(state => state.roles);
const { breadcrumbMap, menuData } = useMemo(() => {
- const _menus = menus.filter(
+ const _menus = menusTree.filter(
item => (item.isAdmin && roles?.includes('ADMIN')) || !item.isAdmin,
);
return getMenuData(_menus);
diff --git a/inlong-dashboard/src/configs/locales/index.ts
b/inlong-dashboard/src/configs/locales/index.ts
index acf6c67b8..e4c48f3ed 100644
--- a/inlong-dashboard/src/configs/locales/index.ts
+++ b/inlong-dashboard/src/configs/locales/index.ts
@@ -20,7 +20,7 @@
interface LocalesType {
[key: string]: {
label: string;
- antdPath: string;
+ uiComponentPath: string;
dayjsPath: string;
};
}
@@ -28,12 +28,12 @@ interface LocalesType {
export const localesConfig: LocalesType = {
cn: {
label: '简体中文',
- antdPath: 'zh_CN',
+ uiComponentPath: 'zh_CN',
dayjsPath: 'zh-cn',
},
en: {
label: 'English',
- antdPath: 'en_US',
+ uiComponentPath: 'en_US',
dayjsPath: 'en',
},
};
diff --git a/inlong-dashboard/src/configs/menus/index.ts
b/inlong-dashboard/src/configs/menus/index.ts
index ed6c28c86..25d011bcd 100644
--- a/inlong-dashboard/src/configs/menus/index.ts
+++ b/inlong-dashboard/src/configs/menus/index.ts
@@ -18,15 +18,33 @@
*/
import i18n from '@/i18n';
+import { treeToArray } from '@/utils';
export interface MenuItemType {
name: string;
+ key?: string; // auto generate
+ deepKey?: string; // auto generate
children?: MenuItemType[];
path?: string;
isAdmin?: boolean;
}
-const menus: MenuItemType[] = [
+const genMenuKey = (array: Omit<MenuItemType, 'key'>[], parentKey = ''):
MenuItemType[] => {
+ return array.map((item, index) => {
+ let obj = { ...item };
+ const num = index + 1 < 10 ? `0${index + 1}` : (index + 1).toString();
+ const currentKey = `${parentKey}${num}`;
+ if (obj.children) {
+ obj.children = genMenuKey(obj.children, currentKey);
+ }
+ return {
+ ...obj,
+ key: currentKey,
+ };
+ });
+};
+
+const menusTree: MenuItemType[] = genMenuKey([
{
path: '/group',
name: i18n.t('configs.menus.Groups'),
@@ -70,6 +88,8 @@ const menus: MenuItemType[] = [
},
],
},
-];
+]);
+
+export const menuArrays: Omit<MenuItemType, 'children'>[] =
treeToArray(menusTree, 'key', 'pKey');
-export default menus;
+export default menusTree;
diff --git a/inlong-dashboard/src/metas/DataWithBackend.ts
b/inlong-dashboard/src/metas/DataWithBackend.ts
index 8104b17d0..8a07f6114 100644
--- a/inlong-dashboard/src/metas/DataWithBackend.ts
+++ b/inlong-dashboard/src/metas/DataWithBackend.ts
@@ -23,4 +23,12 @@ export abstract class DataWithBackend extends DataStatic {
abstract parse<T, K>(data: T): K;
abstract stringify<T, K>(data: T): K;
+
+ abstract post?<T, K>(data: T): Promise<K>;
+
+ abstract delete?<T, K>(data: T): Promise<K>;
+
+ abstract put?<T, K>(data: T): Promise<K>;
+
+ abstract get?<T, K>(data: T): Promise<K>;
}
diff --git a/inlong-dashboard/src/models/index.ts
b/inlong-dashboard/src/models/index.ts
index fed19ff82..09eab7b58 100644
--- a/inlong-dashboard/src/models/index.ts
+++ b/inlong-dashboard/src/models/index.ts
@@ -19,7 +19,7 @@
import { createStore } from 'redux';
import { pathToRegexp } from 'path-to-regexp';
-import menus from '@/configs/menus';
+import { menuArrays, MenuItemType } from '@/configs/menus';
import { getPathnameExist } from '@/configs/routes';
import { getCurrentLocale } from '@/configs/locales';
@@ -28,10 +28,7 @@ export interface State {
userName: string;
userId: number;
roles: string[];
- currentMenu: null | {
- name: string;
- path: string;
- };
+ currentMenu: null | Omit<MenuItemType, 'children'>;
}
const state: State = {
@@ -65,7 +62,7 @@ const reducers = {
// Find the selected route
let currentMenu = null;
- for (const item of menus) {
+ for (const item of menuArrays) {
if (
item.path &&
// The route in the menu || is not in the menu, but belongs to a
sub-route under a menu
diff --git a/inlong-dashboard/src/router.tsx b/inlong-dashboard/src/router.tsx
index e44f8d335..80fe60d8c 100644
--- a/inlong-dashboard/src/router.tsx
+++ b/inlong-dashboard/src/router.tsx
@@ -104,7 +104,7 @@ const App = () => {
const importLocale = useCallback(async locale => {
if (!localesConfig[locale]) return;
- const { antdPath, dayjsPath } = localesConfig[locale];
+ const { uiComponentPath, dayjsPath } = localesConfig[locale];
const [messagesDefault, messagesExtends, antdMessages] = await
Promise.all([
import(
/* webpackChunkName: 'default-locales-[request]' */
@@ -117,7 +117,7 @@ const App = () => {
import(
/* webpackInclude: /(zh_CN|en_US)\.js$/ */
/* webpackChunkName: 'antd-locales-[request]' */
- `antd/es/locale/${antdPath}.js`
+ `antd/es/locale/${uiComponentPath}.js`
),
import(
/* webpackInclude: /(zh-cn|en)\.js$/ */
diff --git a/inlong-dashboard/src/utils/index.ts
b/inlong-dashboard/src/utils/index.ts
index 59a94fa64..ee05e361b 100644
--- a/inlong-dashboard/src/utils/index.ts
+++ b/inlong-dashboard/src/utils/index.ts
@@ -17,6 +17,8 @@
* under the License.
*/
+import cloneDeep from 'lodash/cloneDeep';
+
export function isDevelopEnv() {
if (process.env.NODE_ENV === 'development') {
return true;
@@ -206,3 +208,36 @@ export function getStrByteLen(str: string): number {
}
return len;
}
+
+function treeToArrayHelper(
+ tree: any[],
+ id: string,
+ pid: string,
+ children: string,
+ parent: any = null,
+): any[] {
+ let data = cloneDeep(tree);
+ return data.reduce((accumulator, item) => {
+ const { [children]: itemChildren } = item;
+ let result;
+ if (parent && !item[pid]) {
+ item[pid] = parent[id];
+ }
+ if (parent) {
+ item.deepKey = parent.deepKey.concat(item[id]);
+ } else {
+ item.deepKey = [item[id]];
+ }
+ if (itemChildren) {
+ result = accumulator.concat(item, treeToArrayHelper(itemChildren, id,
pid, children, item));
+ delete item[children];
+ } else {
+ result = accumulator.concat(item);
+ }
+ return result;
+ }, []);
+}
+
+export function treeToArray(tree: any[], id = 'id', pid = 'pid', children =
'children') {
+ return treeToArrayHelper(tree, id, pid, children);
+}