This is an automated email from the ASF dual-hosted git repository.
likyh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new f3f6e9be2 feat(config-ui): show connection name in blueprint detail
(#5214)
f3f6e9be2 is described below
commit f3f6e9be22cc9eee450ab07d0314c2e3edb95d47
Author: 青湛 <[email protected]>
AuthorDate: Thu May 18 09:33:25 2023 +0800
feat(config-ui): show connection name in blueprint detail (#5214)
* refactor(config-ui): move connection-status to component from store
* refactor(config-ui): split tips store content
* fix(config-ui): adjust the type defined in table component
* refactor(config-ui): the connection store
* fix(config-ui): the style for bp detail
* fix(config-ui): the type defined
* refactor(config-ui): move connection-status to plugin/component from
component
---
.../components/table/hooks/use-row-selection.ts | 10 +-
config-ui/src/hooks/index.ts | 2 +
config-ui/src/hooks/use-connections.ts | 47 +++++
config-ui/src/hooks/{index.ts => use-tips.ts} | 8 +-
config-ui/src/layouts/base/base.tsx | 201 ++++++++++-----------
config-ui/src/main.tsx | 7 +-
.../blueprint/connection-add/components/step-1.tsx | 78 ++++----
.../pages/blueprint/create/components/step-1.tsx | 5 +-
config-ui/src/pages/blueprint/create/index.tsx | 43 ++---
.../detail/components/connection-list/index.tsx | 14 +-
config-ui/src/pages/blueprint/detail/styled.ts | 8 +
config-ui/src/pages/connection/detail/index.tsx | 22 +--
config-ui/src/pages/connection/home/index.tsx | 7 +-
.../plugins/components/connection-list/index.tsx | 5 +-
.../components/connection-status/index.tsx} | 3 +-
config-ui/src/plugins/components/index.ts | 1 +
.../components/transformation-select/index.tsx | 2 +-
.../plugins/components/transformation/index.tsx | 2 +-
.../plugins/register/webook/connection/index.tsx | 17 +-
.../register/webook/connection/use-connection.ts | 54 ------
config-ui/src/store/connections/context.tsx | 148 +++++++++++++--
config-ui/src/store/connections/index.ts | 1 -
config-ui/src/store/connections/types.ts | 4 +-
.../src/store/connections/use-context-value.ts | 159 ----------------
config-ui/src/store/tips/context.tsx | 22 +--
25 files changed, 405 insertions(+), 465 deletions(-)
diff --git a/config-ui/src/components/table/hooks/use-row-selection.ts
b/config-ui/src/components/table/hooks/use-row-selection.ts
index a0a686f5e..358122e48 100644
--- a/config-ui/src/components/table/hooks/use-row-selection.ts
+++ b/config-ui/src/components/table/hooks/use-row-selection.ts
@@ -23,13 +23,13 @@ export interface UseRowSelectionProps<T> {
rowSelection?: {
rowKey: string;
type?: 'checkbox' | 'radio';
- selectedRowKeys?: ID[];
- onChange?: (selectedRowKeys: ID[]) => void;
+ selectedRowKeys?: string[];
+ onChange?: (selectedRowKeys: string[]) => void;
};
}
export const useRowSelection = <T>({ dataSource, rowSelection }:
UseRowSelectionProps<T>) => {
- const [selectedKeys, setSelectedKeys] = useState<ID[]>([]);
+ const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
const {
rowKey = 'key',
@@ -48,7 +48,7 @@ export const useRowSelection = <T>({ dataSource, rowSelection
}: UseRowSelection
const handleChecked = (data: T) => {
const key = (data as any)[rowKey];
- let result: ID[] = selectedKeys;
+ let result: string[] = selectedKeys;
switch (true) {
case !selectedKeys.includes(key) && type === 'radio':
@@ -66,7 +66,7 @@ export const useRowSelection = <T>({ dataSource, rowSelection
}: UseRowSelection
};
const handleCheckedAll = () => {
- let result: ID[] = [];
+ let result: string[] = [];
if (selectedKeys.length !== dataSource.length) {
result = dataSource.map((data: any) => data[rowKey]);
diff --git a/config-ui/src/hooks/index.ts b/config-ui/src/hooks/index.ts
index b0440f66e..cba922842 100644
--- a/config-ui/src/hooks/index.ts
+++ b/config-ui/src/hooks/index.ts
@@ -17,5 +17,7 @@
*/
export * from './use-auto-refresh';
+export * from './use-connections';
export * from './use-refresh-data';
+export * from './use-tips';
export * from './user-proxy-prefix';
diff --git a/config-ui/src/hooks/use-connections.ts
b/config-ui/src/hooks/use-connections.ts
new file mode 100644
index 000000000..f129c13ee
--- /dev/null
+++ b/config-ui/src/hooks/use-connections.ts
@@ -0,0 +1,47 @@
+/*
+ * 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 { useContext } from 'react';
+
+import { ConnectionContext } from '@/store';
+
+type UseConnectionsProps = {
+ unique?: string;
+ plugin?: string;
+ filter?: string[];
+ filterBeta?: boolean;
+ filterPlugin?: string[];
+};
+
+export const useConnections = (props?: UseConnectionsProps) => {
+ const { unique, plugin, filter, filterBeta, filterPlugin } = props || {};
+
+ const { connections, onGet, onTest, onRefresh } =
useContext(ConnectionContext);
+
+ return {
+ connection: unique ? connections.find((cs) => cs.unique === unique) : null,
+ connections: connections
+ .filter((cs) => (plugin ? cs.plugin === plugin : true))
+ .filter((cs) => (filter ? !filter.includes(cs.unique) : true))
+ .filter((cs) => (filterBeta ? !cs.isBeta : true))
+ .filter((cs) => (filterPlugin ? !filterPlugin.includes(cs.plugin) :
true)),
+ onGet,
+ onTest,
+ onRefresh,
+ };
+};
diff --git a/config-ui/src/hooks/index.ts b/config-ui/src/hooks/use-tips.ts
similarity index 86%
copy from config-ui/src/hooks/index.ts
copy to config-ui/src/hooks/use-tips.ts
index b0440f66e..2478efade 100644
--- a/config-ui/src/hooks/index.ts
+++ b/config-ui/src/hooks/use-tips.ts
@@ -16,6 +16,8 @@
*
*/
-export * from './use-auto-refresh';
-export * from './use-refresh-data';
-export * from './user-proxy-prefix';
+import { useContext } from 'react';
+
+import { TipsContext } from '@/store';
+
+export const useTips = () => useContext(TipsContext);
diff --git a/config-ui/src/layouts/base/base.tsx
b/config-ui/src/layouts/base/base.tsx
index 8b5d8a069..b612b1496 100644
--- a/config-ui/src/layouts/base/base.tsx
+++ b/config-ui/src/layouts/base/base.tsx
@@ -21,8 +21,7 @@ import { useLocation } from 'react-router-dom';
import { Menu, MenuItem, Tag, Navbar, Intent, Alignment, Button } from
'@blueprintjs/core';
import { PageLoading, Logo, ExternalLink } from '@/components';
-import { useRefreshData } from '@/hooks';
-import { TipsContextProvider, TipsContextConsumer } from '@/store';
+import { useTips, useRefreshData } from '@/hooks';
import { history } from '@/utils/history';
import DashboardIcon from '@/images/icons/dashborad.svg';
@@ -42,7 +41,7 @@ interface Props {
export const BaseLayout = ({ children }: Props) => {
const menu = useMenu();
const { pathname } = useLocation();
-
+ const { tips } = useTips();
const { ready, data } = useRefreshData<{ version: string }>(() =>
API.getVersion(), []);
const token = window.localStorage.getItem('accessToken');
@@ -72,106 +71,100 @@ export const BaseLayout = ({ children }: Props) => {
}
return (
- <TipsContextProvider>
- <TipsContextConsumer>
- {({ text }) => (
- <S.Wrapper>
- <S.Sider>
- <Logo />
- <Menu className="menu">
- {menu.map((it) => {
- const paths = [it.path, ...(it.children ?? []).map((cit) =>
cit.path)];
- const active = !!paths.find((path) =>
pathname.includes(path));
- return (
- <MenuItem
- key={it.key}
- className="menu-item"
- text={it.title}
- icon={it.icon}
- active={active}
- onClick={() => handlePushPath(it)}
- >
- {it.children?.map((cit) => (
- <MenuItem
- key={cit.key}
- className="sub-menu-item"
- text={
- <S.SiderMenuItem>
- <span>{cit.title}</span>
- {cit.isBeta && <Tag
intent={Intent.WARNING}>beta</Tag>}
- </S.SiderMenuItem>
- }
- icon={cit.icon ?? <img src={cit.iconUrl} width={16}
alt="" />}
- active={pathname.includes(cit.path)}
- disabled={cit.disabled}
- onClick={() => handlePushPath(cit)}
- />
- ))}
- </MenuItem>
- );
- })}
- </Menu>
- <div className="copyright">
- <div>Apache 2.0 License</div>
- <div className="version">{data.version}</div>
- </div>
- </S.Sider>
- <S.Main>
- <S.Header>
- <Navbar.Group align={Alignment.RIGHT}>
- <S.DashboardIcon>
- <ExternalLink link={getGrafanaUrl()}>
- <img src={DashboardIcon} alt="dashboards" />
- <span>Dashboards</span>
- </ExternalLink>
- </S.DashboardIcon>
- <Navbar.Divider />
- <a
href="https://devlake.apache.org/docs/Configuration/Tutorial" rel="noreferrer"
target="_blank">
- <img src={FileIcon} alt="documents" />
- <span>Docs</span>
- </a>
- <Navbar.Divider />
- <ExternalLink link="/api/swagger/index.html">
- <img src={APIIcon} alt="api" />
- <span>API</span>
- </ExternalLink>
- <Navbar.Divider />
- <a
- href="https://github.com/apache/incubator-devlake"
- rel="noreferrer"
- target="_blank"
- className="navIconLink"
- >
- <img src={GitHubIcon} alt="github" />
- <span>GitHub</span>
- </a>
- <Navbar.Divider />
- <a
-
href="https://join.slack.com/t/devlake-io/shared_invite/zt-17b6vuvps-x98pqseoUagM7EAmKC82xQ"
- rel="noreferrer"
- target="_blank"
- >
- <img src={SlackIcon} alt="slack" />
- <span>Slack</span>
- </a>
- {token && (
- <>
- <Navbar.Divider />
- <Button small intent={Intent.NONE}
onClick={handleSignOut}>
- Sign Out
- </Button>
- </>
- )}
- </Navbar.Group>
- </S.Header>
- <S.Inner>
- <S.Content>{children}</S.Content>
- </S.Inner>
- {text && <S.Tips>{text}</S.Tips>}
- </S.Main>
- </S.Wrapper>
- )}
- </TipsContextConsumer>
- </TipsContextProvider>
+ <S.Wrapper>
+ <S.Sider>
+ <Logo />
+ <Menu className="menu">
+ {menu.map((it) => {
+ const paths = [it.path, ...(it.children ?? []).map((cit) =>
cit.path)];
+ const active = !!paths.find((path) => pathname.includes(path));
+ return (
+ <MenuItem
+ key={it.key}
+ className="menu-item"
+ text={it.title}
+ icon={it.icon}
+ active={active}
+ onClick={() => handlePushPath(it)}
+ >
+ {it.children?.map((cit) => (
+ <MenuItem
+ key={cit.key}
+ className="sub-menu-item"
+ text={
+ <S.SiderMenuItem>
+ <span>{cit.title}</span>
+ {cit.isBeta && <Tag intent={Intent.WARNING}>beta</Tag>}
+ </S.SiderMenuItem>
+ }
+ icon={cit.icon ?? <img src={cit.iconUrl} width={16} alt=""
/>}
+ active={pathname.includes(cit.path)}
+ disabled={cit.disabled}
+ onClick={() => handlePushPath(cit)}
+ />
+ ))}
+ </MenuItem>
+ );
+ })}
+ </Menu>
+ <div className="copyright">
+ <div>Apache 2.0 License</div>
+ <div className="version">{data.version}</div>
+ </div>
+ </S.Sider>
+ <S.Main>
+ <S.Header>
+ <Navbar.Group align={Alignment.RIGHT}>
+ <S.DashboardIcon>
+ <ExternalLink link={getGrafanaUrl()}>
+ <img src={DashboardIcon} alt="dashboards" />
+ <span>Dashboards</span>
+ </ExternalLink>
+ </S.DashboardIcon>
+ <Navbar.Divider />
+ <a href="https://devlake.apache.org/docs/Configuration/Tutorial"
rel="noreferrer" target="_blank">
+ <img src={FileIcon} alt="documents" />
+ <span>Docs</span>
+ </a>
+ <Navbar.Divider />
+ <ExternalLink link="/api/swagger/index.html">
+ <img src={APIIcon} alt="api" />
+ <span>API</span>
+ </ExternalLink>
+ <Navbar.Divider />
+ <a
+ href="https://github.com/apache/incubator-devlake"
+ rel="noreferrer"
+ target="_blank"
+ className="navIconLink"
+ >
+ <img src={GitHubIcon} alt="github" />
+ <span>GitHub</span>
+ </a>
+ <Navbar.Divider />
+ <a
+
href="https://join.slack.com/t/devlake-io/shared_invite/zt-17b6vuvps-x98pqseoUagM7EAmKC82xQ"
+ rel="noreferrer"
+ target="_blank"
+ >
+ <img src={SlackIcon} alt="slack" />
+ <span>Slack</span>
+ </a>
+ {token && (
+ <>
+ <Navbar.Divider />
+ <Button small intent={Intent.NONE} onClick={handleSignOut}>
+ Sign Out
+ </Button>
+ </>
+ )}
+ </Navbar.Group>
+ </S.Header>
+ <S.Inner>
+ <S.Content>{children}</S.Content>
+ </S.Inner>
+ {tips && <S.Tips>{tips}</S.Tips>}
+ </S.Main>
+ </S.Wrapper>
);
};
diff --git a/config-ui/src/main.tsx b/config-ui/src/main.tsx
index 96e41f49b..5108af853 100644
--- a/config-ui/src/main.tsx
+++ b/config-ui/src/main.tsx
@@ -20,6 +20,7 @@ import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { ErrorBoundary } from '@/components';
+import { ConnectionContextProvider, TipsContextProvider } from '@/store';
import App from './App';
@@ -28,7 +29,11 @@ import './index.css';
ReactDOM.render(
<BrowserRouter>
<ErrorBoundary>
- <App />
+ <ConnectionContextProvider>
+ <TipsContextProvider>
+ <App />
+ </TipsContextProvider>
+ </ConnectionContextProvider>
</ErrorBoundary>
</BrowserRouter>,
document.getElementById('root'),
diff --git a/config-ui/src/pages/blueprint/connection-add/components/step-1.tsx
b/config-ui/src/pages/blueprint/connection-add/components/step-1.tsx
index f0b63dbbd..3a1bb2263 100644
--- a/config-ui/src/pages/blueprint/connection-add/components/step-1.tsx
+++ b/config-ui/src/pages/blueprint/connection-add/components/step-1.tsx
@@ -19,9 +19,8 @@
import { Button, Intent } from '@blueprintjs/core';
import { Table } from '@/components';
+import { useConnections } from '@/hooks';
import { getPluginConfig } from '@/plugins';
-import type { ConnectionItemType } from '@/store';
-import { ConnectionContextProvider, ConnectionContextConsumer } from '@/store';
import { useConnectionAdd } from '../context';
@@ -29,47 +28,42 @@ import * as S from './styled';
export const Step1 = () => {
const { filter, connection, onChangeConnection, onCancel, onNext } =
useConnectionAdd();
+ const { connections, onGet } = useConnections({ filter, filterBeta: true,
filterPlugin: ['webhook'] });
return (
- <ConnectionContextProvider filterBeta filterPlugin={['webhook']}
filter={filter}>
- <ConnectionContextConsumer>
- {({ connections }) => (
- <S.Wrapper>
- <Table
- columns={[{ title: 'Data Connection', dataIndex: 'name', key:
'name' }]}
- dataSource={connections}
- rowSelection={{
- rowKey: 'unique',
- type: 'radio',
- selectedRowKeys: connection?.unique ? [connection?.unique] :
[],
- onChange: (selectedRowKeys) => {
- const unique = selectedRowKeys[0];
- const connection = connections.find((cs) => cs.unique ===
unique) as ConnectionItemType;
- const config = getPluginConfig(connection.plugin);
- onChangeConnection({
- unique: connection.unique,
- plugin: connection.plugin,
- connectionId: connection.id,
- name: connection.name,
- icon: connection.icon,
- scope: [],
- origin: [],
- transformationType: config.transformationType,
- });
- },
- }}
- />
- <S.Action>
- <Button outlined intent={Intent.PRIMARY} onClick={onCancel}>
- Cancel
- </Button>
- <Button intent={Intent.PRIMARY} disabled={!connection}
onClick={onNext}>
- Next Step
- </Button>
- </S.Action>
- </S.Wrapper>
- )}
- </ConnectionContextConsumer>
- </ConnectionContextProvider>
+ <S.Wrapper>
+ <Table
+ columns={[{ title: 'Data Connection', dataIndex: 'name', key: 'name'
}]}
+ dataSource={connections}
+ rowSelection={{
+ rowKey: 'unique',
+ type: 'radio',
+ selectedRowKeys: connection?.unique ? [connection?.unique] : [],
+ onChange: (selectedRowKeys) => {
+ const unique = selectedRowKeys[0];
+ const connection = onGet(unique);
+ const config = getPluginConfig(connection.plugin);
+ onChangeConnection({
+ unique: connection.unique,
+ plugin: connection.plugin,
+ connectionId: connection.id,
+ name: connection.name,
+ icon: connection.icon,
+ scope: [],
+ origin: [],
+ transformationType: config.transformationType,
+ });
+ },
+ }}
+ />
+ <S.Action>
+ <Button outlined intent={Intent.PRIMARY} onClick={onCancel}>
+ Cancel
+ </Button>
+ <Button intent={Intent.PRIMARY} disabled={!connection}
onClick={onNext}>
+ Next Step
+ </Button>
+ </S.Action>
+ </S.Wrapper>
);
};
diff --git a/config-ui/src/pages/blueprint/create/components/step-1.tsx
b/config-ui/src/pages/blueprint/create/components/step-1.tsx
index 1e7353296..a546dd44b 100644
--- a/config-ui/src/pages/blueprint/create/components/step-1.tsx
+++ b/config-ui/src/pages/blueprint/create/components/step-1.tsx
@@ -21,8 +21,9 @@ import { Link } from 'react-router-dom';
import { InputGroup, Icon, Button, Intent } from '@blueprintjs/core';
import { Card, Divider, MultiSelector, Loading } from '@/components';
+import { useConnections } from '@/hooks';
import { getPluginConfig } from '@/plugins';
-import { useConnection, ConnectionStatusEnum } from '@/store';
+import { ConnectionStatusEnum } from '@/store';
import { ModeEnum, FromEnum } from '../../types';
import { AdvancedEditor } from '../../components';
@@ -37,7 +38,7 @@ interface Props {
}
export const Step1 = ({ from }: Props) => {
- const { connections, onTest } = useConnection();
+ const { connections, onTest } = useConnections({ filterBeta: true,
filterPlugin: ['webhook'] });
const { mode, name, rawPlan, onChangeMode, onChangeName,
onChangeConnections, onChangeRawPlan, onNext, ...props } =
useCreate();
diff --git a/config-ui/src/pages/blueprint/create/index.tsx
b/config-ui/src/pages/blueprint/create/index.tsx
index 347bf6aaa..f9f788e31 100644
--- a/config-ui/src/pages/blueprint/create/index.tsx
+++ b/config-ui/src/pages/blueprint/create/index.tsx
@@ -20,7 +20,6 @@ import { useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { PageHeader, Workflow } from '@/components';
-import { ConnectionContextProvider } from '@/store';
import { ModeEnum, FromEnum } from '../types';
@@ -53,27 +52,25 @@ export const BlueprintCreatePage = ({ from }: Props) => {
);
return (
- <ConnectionContextProvider filterBeta filterPlugin={['webhook']}>
- <ContextProvider from={from} projectName={pname}>
- <Context.Consumer>
- {({ step, mode }) => (
- <PageHeader breadcrumbs={breadcrumbs}>
- <Workflow
- steps={
- mode === ModeEnum.normal
- ? ['Add Data Connections', 'Set Data Scope', 'Add
Transformation (Optional)', 'Set Sync Policy']
- : ['Create Advanced Configuration', 'Set Sync Policy']
- }
- activeStep={step}
- />
- {step === 1 && <Step1 from={from} />}
- {mode === ModeEnum.normal && step === 2 && <Step2 />}
- {step === 3 && <Step3 />}
- {((mode === ModeEnum.normal && step === 4) || (mode ===
ModeEnum.advanced && step === 2)) && <Step4 />}
- </PageHeader>
- )}
- </Context.Consumer>
- </ContextProvider>
- </ConnectionContextProvider>
+ <ContextProvider from={from} projectName={pname}>
+ <Context.Consumer>
+ {({ step, mode }) => (
+ <PageHeader breadcrumbs={breadcrumbs}>
+ <Workflow
+ steps={
+ mode === ModeEnum.normal
+ ? ['Add Data Connections', 'Set Data Scope', 'Add
Transformation (Optional)', 'Set Sync Policy']
+ : ['Create Advanced Configuration', 'Set Sync Policy']
+ }
+ activeStep={step}
+ />
+ {step === 1 && <Step1 from={from} />}
+ {mode === ModeEnum.normal && step === 2 && <Step2 />}
+ {step === 3 && <Step3 />}
+ {((mode === ModeEnum.normal && step === 4) || (mode ===
ModeEnum.advanced && step === 2)) && <Step4 />}
+ </PageHeader>
+ )}
+ </Context.Consumer>
+ </ContextProvider>
);
};
diff --git
a/config-ui/src/pages/blueprint/detail/components/connection-list/index.tsx
b/config-ui/src/pages/blueprint/detail/components/connection-list/index.tsx
index 034d180d6..91acde78a 100644
--- a/config-ui/src/pages/blueprint/detail/components/connection-list/index.tsx
+++ b/config-ui/src/pages/blueprint/detail/components/connection-list/index.tsx
@@ -19,6 +19,7 @@
import { useMemo } from 'react';
import { Link } from 'react-router-dom';
+import { useConnections } from '@/hooks';
import { getPluginConfig } from '@/plugins';
import type { BlueprintType } from '../../../types';
@@ -31,16 +32,21 @@ interface Props {
}
export const ConnectionList = ({ path, blueprint }: Props) => {
- const connections = useMemo(
+ const { onGet } = useConnections();
+
+ const list = useMemo(
() =>
blueprint.settings?.connections
.filter((cs) => cs.plugin !== 'webhook')
.map((cs: any) => {
+ const unique = `${cs.plugin}-${cs.connectionId}`;
const plugin = getPluginConfig(cs.plugin);
+ const connection = onGet(unique);
+
return {
- unique: `${cs.plugin}-${cs.connectionId}`,
+ unique,
icon: plugin.icon,
- name: plugin.name,
+ name: connection.name,
scope: cs.scopes,
};
})
@@ -50,7 +56,7 @@ export const ConnectionList = ({ path, blueprint }: Props) =>
{
return (
<S.List>
- {connections.map((cs) => (
+ {list.map((cs) => (
<S.Item key={cs.unique}>
<div className="title">
<img src={cs.icon} alt="" />
diff --git a/config-ui/src/pages/blueprint/detail/styled.ts
b/config-ui/src/pages/blueprint/detail/styled.ts
index 28e67fef9..b865d3c8d 100644
--- a/config-ui/src/pages/blueprint/detail/styled.ts
+++ b/config-ui/src/pages/blueprint/detail/styled.ts
@@ -23,6 +23,10 @@ export const Wrapper = styled.div`
`;
export const ConfigurationPanel = styled.div`
+ h3 {
+ margin-bottom: 16px;
+ }
+
.top {
display: flex;
align-items: flex-start;
@@ -90,6 +94,10 @@ export const ActionColumn = styled.div`
`;
export const StatusPanel = styled.div`
+ h3 {
+ margin-bottom: 16px;
+ }
+
& > .info {
display: flex;
justify-content: flex-end;
diff --git a/config-ui/src/pages/connection/detail/index.tsx
b/config-ui/src/pages/connection/detail/index.tsx
index 743b08174..7e3d3e9fe 100644
--- a/config-ui/src/pages/connection/detail/index.tsx
+++ b/config-ui/src/pages/connection/detail/index.tsx
@@ -22,10 +22,8 @@ import { Button, Icon, Intent } from '@blueprintjs/core';
import { PageHeader, Dialog, IconButton, Table } from '@/components';
import { transformEntities } from '@/config';
-import { useRefreshData } from '@/hooks';
-import { ConnectionForm, DataScopeForm2 } from '@/plugins';
-import type { ConnectionItemType } from '@/store';
-import { ConnectionContextProvider, useConnection, ConnectionStatus, useTips }
from '@/store';
+import { useTips, useConnections, useRefreshData } from '@/hooks';
+import { ConnectionForm, ConnectionStatus, DataScopeForm2 } from '@/plugins';
import { operator } from '@/utils';
import * as API from './api';
@@ -42,20 +40,18 @@ const ConnectionDetail = ({ plugin, id }: Props) => {
const [version, setVersion] = useState(1);
const history = useHistory();
- const { connections, onRefresh, onTest } = useConnection();
- const { setText } = useTips();
+ const { onGet, onTest, onRefresh } = useConnections();
+ const { setTips } = useTips();
const { ready, data } = useRefreshData(() => API.getDataScope(plugin, id),
[version]);
- const { unique, status, name, icon, entities } = connections.find(
- (cs) => cs.unique === `${plugin}-${id}`,
- ) as ConnectionItemType;
+ const { unique, status, name, icon, entities } = onGet(`${plugin}-${id}`);
const handleHideDialog = () => {
setType(undefined);
};
const handleShowTips = () => {
- setText(
+ setTips(
<div>
<Icon icon="warning-sign" style={{ marginRight: 8 }} color="#F4BE55" />
<span>
@@ -212,9 +208,5 @@ const ConnectionDetail = ({ plugin, id }: Props) => {
export const ConnectionDetailPage = () => {
const { plugin, id } = useParams<{ plugin: string; id: string }>();
- return (
- <ConnectionContextProvider plugin={plugin}>
- <ConnectionDetail plugin={plugin} id={+id} />
- </ConnectionContextProvider>
- );
+ return <ConnectionDetail plugin={plugin} id={+id} />;
};
diff --git a/config-ui/src/pages/connection/home/index.tsx
b/config-ui/src/pages/connection/home/index.tsx
index 1462bd838..9613bfca5 100644
--- a/config-ui/src/pages/connection/home/index.tsx
+++ b/config-ui/src/pages/connection/home/index.tsx
@@ -20,9 +20,10 @@ import { useState, useMemo } from 'react';
import { Tag, Intent } from '@blueprintjs/core';
import { Dialog } from '@/components';
+import { useConnections } from '@/hooks';
import type { PluginConfigType } from '@/plugins';
-import { PluginConfig, PluginType, ConnectionForm, ConnectionList } from
'@/plugins';
-import { ConnectionContextProvider, useConnection } from '@/store';
+import { PluginConfig, PluginType, ConnectionList, ConnectionForm } from
'@/plugins';
+import { ConnectionContextProvider } from '@/store';
import * as S from './styled';
@@ -30,7 +31,7 @@ export const ConnectionHome = () => {
const [type, setType] = useState<'list' | 'form'>();
const [pluginConfig, setPluginConfig] = useState<PluginConfigType>();
- const { connections, onRefresh } = useConnection();
+ const { connections, onRefresh } = useConnections();
const [plugins, webhook] = useMemo(
() => [
diff --git a/config-ui/src/plugins/components/connection-list/index.tsx
b/config-ui/src/plugins/components/connection-list/index.tsx
index 00fc04e5e..6e5fe4042 100644
--- a/config-ui/src/plugins/components/connection-list/index.tsx
+++ b/config-ui/src/plugins/components/connection-list/index.tsx
@@ -20,7 +20,8 @@ import { Link } from 'react-router-dom';
import { Button, Intent } from '@blueprintjs/core';
import { Table } from '@/components';
-import { ConnectionStatus, useConnection } from '@/store';
+import { useConnections } from '@/hooks';
+import { ConnectionStatus } from '@/plugins';
import { WebHookConnection } from '@/plugins/register/webook';
@@ -38,7 +39,7 @@ export const ConnectionList = ({ plugin, onCreate }: Props)
=> {
};
const BaseList = ({ plugin, onCreate }: Props) => {
- const { connections, onTest } = useConnection();
+ const { connections, onTest } = useConnections();
return (
<>
diff --git a/config-ui/src/store/connections/status.tsx
b/config-ui/src/plugins/components/connection-status/index.tsx
similarity index 97%
rename from config-ui/src/store/connections/status.tsx
rename to config-ui/src/plugins/components/connection-status/index.tsx
index fe9681d20..3475194d4 100644
--- a/config-ui/src/store/connections/status.tsx
+++ b/config-ui/src/plugins/components/connection-status/index.tsx
@@ -19,8 +19,7 @@
import styled from 'styled-components';
import { IconButton } from '@/components';
-
-import { ConnectionStatusEnum } from './types';
+import { ConnectionStatusEnum } from '@/store';
const Wrapper = styled.div`
display: inline-flex;
diff --git a/config-ui/src/plugins/components/index.ts
b/config-ui/src/plugins/components/index.ts
index 49b6b13e8..9fa4e8fdd 100644
--- a/config-ui/src/plugins/components/index.ts
+++ b/config-ui/src/plugins/components/index.ts
@@ -18,6 +18,7 @@
export * from './connection-form';
export * from './connection-list';
+export * from './connection-status';
export * from './data-scope';
export * from './data-scope-form';
export * from './data-scope-form-2';
diff --git a/config-ui/src/plugins/components/transformation-select/index.tsx
b/config-ui/src/plugins/components/transformation-select/index.tsx
index dbb57282a..ac1440fd6 100644
--- a/config-ui/src/plugins/components/transformation-select/index.tsx
+++ b/config-ui/src/plugins/components/transformation-select/index.tsx
@@ -121,7 +121,7 @@ export const TransformationSelect = ({
rowSelection={{
rowKey: 'id',
type: 'radio',
- selectedRowKeys: selectedId ? [selectedId] : [],
+ selectedRowKeys: selectedId ? [`${selectedId}`] : [],
onChange: (selectedRowKeys) => setSelectedId(selectedRowKeys[0]),
}}
noShadow
diff --git a/config-ui/src/plugins/components/transformation/index.tsx
b/config-ui/src/plugins/components/transformation/index.tsx
index ebf6bda7e..758892369 100644
--- a/config-ui/src/plugins/components/transformation/index.tsx
+++ b/config-ui/src/plugins/components/transformation/index.tsx
@@ -49,7 +49,7 @@ export const Transformation = ({
onSubmit,
onNext,
}: Props) => {
- const [selected, setSelected] = useState<Record<string, ID[]>>({});
+ const [selected, setSelected] = useState<Record<string, string[]>>({});
const [connection, setConnection] = useState<MixConnection>();
const [tid, setTid] = useState<ID>();
diff --git a/config-ui/src/plugins/register/webook/connection/index.tsx
b/config-ui/src/plugins/register/webook/connection/index.tsx
index d637895a0..211c548a5 100644
--- a/config-ui/src/plugins/register/webook/connection/index.tsx
+++ b/config-ui/src/plugins/register/webook/connection/index.tsx
@@ -16,32 +16,32 @@
*
*/
-import React, { useState } from 'react';
+import { useState } from 'react';
import { ButtonGroup, Button, Intent } from '@blueprintjs/core';
import { Table, ColumnType, ExternalLink, IconButton } from '@/components';
+import { useConnections } from '@/hooks';
import type { WebhookItemType } from '../types';
import { WebhookCreateDialog } from '../create-dialog';
import { WebhookDeleteDialog } from '../delete-dialog';
import { WebhookViewOrEditDialog } from '../view-or-edit-dialog';
-import type { UseConnectionProps } from './use-connection';
-import { useConnection } from './use-connection';
import * as S from './styled';
type Type = 'add' | 'edit' | 'show' | 'delete';
-interface Props extends UseConnectionProps {
+interface Props {
+ filterIds?: ID[];
onCreateAfter?: (id: ID) => void;
onDeleteAfter?: (id: ID) => void;
}
-export const WebHookConnection = ({ onCreateAfter, onDeleteAfter, ...props }:
Props) => {
+export const WebHookConnection = ({ filterIds, onCreateAfter, onDeleteAfter }:
Props) => {
const [type, setType] = useState<Type>();
const [record, setRecord] = useState<WebhookItemType>();
- const { loading, connections, onRefresh } = useConnection({ ...props });
+ const { connections, onRefresh } = useConnections({ plugin: 'webhook' });
const handleHideDialog = () => {
setType(undefined);
@@ -85,9 +85,8 @@ export const WebHookConnection = ({ onCreateAfter,
onDeleteAfter, ...props }: Pr
<Button icon="plus" text="Add a Webhook" intent={Intent.PRIMARY}
onClick={() => handleShowDialog('add')} />
</ButtonGroup>
<Table
- loading={loading}
columns={columns}
- dataSource={connections}
+ dataSource={connections.filter((cs) => (filterIds ?
filterIds.includes(cs.id) : true))}
noData={{
text: (
<>
@@ -126,7 +125,7 @@ export const WebHookConnection = ({ onCreateAfter,
onDeleteAfter, ...props }: Pr
isOpen
initialValues={record}
onCancel={handleHideDialog}
- onSubmitAfter={onRefresh}
+ onSubmitAfter={() => onRefresh()}
/>
)}
</S.Wrapper>
diff --git a/config-ui/src/plugins/register/webook/connection/use-connection.ts
b/config-ui/src/plugins/register/webook/connection/use-connection.ts
deleted file mode 100644
index 745b59a7b..000000000
--- a/config-ui/src/plugins/register/webook/connection/use-connection.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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 { useState, useEffect, useMemo } from 'react';
-
-import type { WebhookItemType } from '../types';
-import * as API from '../api';
-
-export interface UseConnectionProps {
- filterIds?: ID[];
-}
-
-export const useConnection = ({ filterIds }: UseConnectionProps) => {
- const [loading, setLoading] = useState(false);
- const [connections, setConnections] = useState<WebhookItemType[]>([]);
-
- const getConnections = async () => {
- setLoading(true);
- try {
- const res = await API.getConnections();
- setConnections(res.filter((cs: any) => (filterIds ?
filterIds.includes(cs.id) : true)));
- } finally {
- setLoading(false);
- }
- };
-
- useEffect(() => {
- getConnections();
- }, []);
-
- return useMemo(
- () => ({
- loading,
- connections,
- onRefresh: getConnections,
- }),
- [loading, connections],
- );
-};
diff --git a/config-ui/src/store/connections/context.tsx
b/config-ui/src/store/connections/context.tsx
index fcd69c661..e0b03adc8 100644
--- a/config-ui/src/store/connections/context.tsx
+++ b/config-ui/src/store/connections/context.tsx
@@ -16,32 +16,143 @@
*
*/
-import React, { useContext } from 'react';
+import React, { useState, useEffect, useMemo } from 'react';
import { PageLoading } from '@/components';
+import type { PluginConfigType } from '@/plugins';
+import { PluginConfig, PluginType } from '@/plugins';
+
import type { ConnectionItemType } from './types';
-import type { UseContextValueProps } from './use-context-value';
-import { useContextValue } from './use-context-value';
+import { ConnectionStatusEnum } from './types';
+import * as API from './api';
-const ConnectionContext = React.createContext<{
+export const ConnectionContext = React.createContext<{
connections: ConnectionItemType[];
- onRefresh: (plugin?: string) => void;
+ onGet: (unique: string) => ConnectionItemType;
onTest: (unique: string) => void;
-}>({
- connections: [],
- onRefresh: () => {},
- onTest: () => {},
-});
+ onRefresh: (plugin?: string) => void;
+}>(undefined!);
-interface Props extends UseContextValueProps {
+interface Props {
children?: React.ReactNode;
}
export const ConnectionContextProvider = ({ children, ...props }: Props) => {
- const { loading, connections, onRefresh, onTest } = useContextValue({
- ...props,
- });
+ const [loading, setLoading] = useState(true);
+ const [connections, setConnections] = useState<ConnectionItemType[]>([]);
+
+ const plugins = useMemo(() => PluginConfig.filter((p) => p.type ===
PluginType.Connection), []);
+
+ const queryConnection = async (plugin: string) => {
+ try {
+ const res = await API.getConnection(plugin);
+ const { name, icon, isBeta, entities } = plugins.find((p) => p.plugin
=== plugin) as PluginConfigType;
+
+ return res.map((connection) => ({
+ ...connection,
+ plugin,
+ pluginName: name,
+ icon,
+ isBeta: isBeta ?? false,
+ entities,
+ }));
+ } catch {
+ return [];
+ }
+ };
+
+ const testConnection = async ({
+ plugin,
+ endpoint,
+ proxy,
+ token,
+ username,
+ password,
+ authMethod,
+ }: ConnectionItemType) => {
+ try {
+ const res = await API.testConnection(plugin, {
+ endpoint,
+ proxy,
+ token,
+ username,
+ password,
+ authMethod,
+ });
+ return res.success ? ConnectionStatusEnum.ONLINE :
ConnectionStatusEnum.OFFLINE;
+ } catch {
+ return ConnectionStatusEnum.OFFLINE;
+ }
+ };
+
+ const transformConnection = (connections: Omit<ConnectionItemType, 'unique'
| 'status'>[]) => {
+ return connections.map((it) => ({
+ unique: `${it.plugin}-${it.id}`,
+ plugin: it.plugin,
+ pluginName: it.pluginName,
+ id: it.id,
+ name: it.name,
+ status: ConnectionStatusEnum.NULL,
+ icon: it.icon,
+ isBeta: it.isBeta,
+ entities: it.entities,
+ endpoint: it.endpoint,
+ proxy: it.proxy,
+ token: it.token,
+ username: it.username,
+ password: it.password,
+ authMethod: it.authMethod,
+ }));
+ };
+
+ const handleGet = (unique: string) => {
+ return connections.find((cs) => cs.unique === unique) as
ConnectionItemType;
+ };
+
+ const handleTest = async (unique: string) => {
+ setConnections((connections) =>
+ connections.map((cs) =>
+ cs.unique === unique
+ ? {
+ ...cs,
+ status: ConnectionStatusEnum.TESTING,
+ }
+ : cs,
+ ),
+ );
+
+ const connection = handleGet(unique);
+ const status = await testConnection(connection);
+
+ setConnections((connections) =>
+ connections.map((cs) =>
+ cs.unique === unique
+ ? {
+ ...cs,
+ status,
+ }
+ : cs,
+ ),
+ );
+ };
+
+ const handleRefresh = async (plugin?: string) => {
+ if (plugin) {
+ const res = await queryConnection(plugin);
+ setConnections([...connections.filter((cs) => cs.plugin !== plugin),
...transformConnection(res)]);
+ return;
+ }
+
+ const res = await Promise.all(plugins.map((cs) =>
queryConnection(cs.plugin)));
+
+ setConnections(transformConnection(res.flat()));
+ setLoading(false);
+ };
+
+ useEffect(() => {
+ handleRefresh();
+ }, []);
if (loading) {
return <PageLoading />;
@@ -51,15 +162,12 @@ export const ConnectionContextProvider = ({ children,
...props }: Props) => {
<ConnectionContext.Provider
value={{
connections,
- onRefresh,
- onTest,
+ onGet: handleGet,
+ onTest: handleTest,
+ onRefresh: handleRefresh,
}}
>
{children}
</ConnectionContext.Provider>
);
};
-
-export const ConnectionContextConsumer = ConnectionContext.Consumer;
-
-export const useConnection = () => useContext(ConnectionContext);
diff --git a/config-ui/src/store/connections/index.ts
b/config-ui/src/store/connections/index.ts
index 4cf5eceeb..34ae5c1d0 100644
--- a/config-ui/src/store/connections/index.ts
+++ b/config-ui/src/store/connections/index.ts
@@ -18,4 +18,3 @@
export * from './types';
export * from './context';
-export * from './status';
diff --git a/config-ui/src/store/connections/types.ts
b/config-ui/src/store/connections/types.ts
index f9c543b7d..cbf96b32a 100644
--- a/config-ui/src/store/connections/types.ts
+++ b/config-ui/src/store/connections/types.ts
@@ -25,11 +25,13 @@ export enum ConnectionStatusEnum {
export type ConnectionItemType = {
unique: string;
- status: ConnectionStatusEnum;
plugin: string;
+ pluginName: string;
id: ID;
name: string;
+ status: ConnectionStatusEnum;
icon: string;
+ isBeta: boolean;
entities: string[];
endpoint: string;
proxy: string;
diff --git a/config-ui/src/store/connections/use-context-value.ts
b/config-ui/src/store/connections/use-context-value.ts
deleted file mode 100644
index 9d7cd8fb8..000000000
--- a/config-ui/src/store/connections/use-context-value.ts
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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 { useState, useEffect, useMemo } from 'react';
-
-import type { PluginConfigType } from '@/plugins';
-import { PluginConfig, PluginType } from '@/plugins';
-
-import type { ConnectionItemType } from './types';
-import { ConnectionStatusEnum } from './types';
-import * as API from './api';
-
-export interface UseContextValueProps {
- plugin?: string;
- filterBeta?: boolean;
- filterPlugin?: string[];
- filter?: string[];
-}
-
-export const useContextValue = ({ plugin, filterBeta, filterPlugin, filter }:
UseContextValueProps) => {
- const [loading, setLoading] = useState(true);
- const [connections, setConnections] = useState<ConnectionItemType[]>([]);
-
- const plugins = useMemo(
- () =>
- PluginConfig.filter((p) => p.type === PluginType.Connection)
- .filter((p) => (plugin ? p.plugin === plugin : true))
- .filter((p) => (filterBeta ? !p.isBeta : true))
- .filter((p) => (filterPlugin ? !filterPlugin.includes(p.plugin) :
true)),
- [plugin],
- );
-
- const getConnection = async (plugin: string) => {
- try {
- const res = await API.getConnection(plugin);
- const { icon, entities } = plugins.find((p) => p.plugin === plugin) as
PluginConfigType;
-
- return res.map((connection) => ({
- ...connection,
- plugin,
- icon,
- entities,
- }));
- } catch {
- return [];
- }
- };
-
- const testConnection = async ({
- plugin,
- endpoint,
- proxy,
- token,
- username,
- password,
- authMethod,
- }: ConnectionItemType) => {
- try {
- const res = await API.testConnection(plugin, {
- endpoint,
- proxy,
- token,
- username,
- password,
- authMethod,
- });
- return res.success ? ConnectionStatusEnum.ONLINE :
ConnectionStatusEnum.OFFLINE;
- } catch {
- return ConnectionStatusEnum.OFFLINE;
- }
- };
-
- const transformConnection = (connections: Omit<ConnectionItemType, 'unique'
| 'status'>[]) => {
- return connections.map((it) => ({
- unique: `${it.plugin}-${it.id}`,
- status: ConnectionStatusEnum.NULL,
- plugin: it.plugin,
- id: it.id,
- name: it.name,
- icon: it.icon,
- entities: it.entities,
- endpoint: it.endpoint,
- proxy: it.proxy,
- token: it.token,
- username: it.username,
- password: it.password,
- authMethod: it.authMethod,
- }));
- };
-
- const handleRefresh = async (plugin?: string) => {
- if (plugin) {
- const res = await getConnection(plugin);
- setConnections([...connections.filter((cs) => cs.plugin !== plugin),
...transformConnection(res)]);
- return;
- }
-
- const res = await Promise.all(plugins.map((cs) =>
getConnection(cs.plugin)));
-
- setConnections(transformConnection(res.flat()));
- setLoading(false);
- };
-
- const handleTest = async (unique: string) => {
- setConnections((connections) =>
- connections.map((cs) =>
- cs.unique === unique
- ? {
- ...cs,
- status: ConnectionStatusEnum.TESTING,
- }
- : cs,
- ),
- );
-
- const connection = connections.find((cs) => cs.unique === unique) as
ConnectionItemType;
- const status = await testConnection(connection);
-
- setConnections((connections) =>
- connections.map((cs) =>
- cs.unique === unique
- ? {
- ...cs,
- status,
- }
- : cs,
- ),
- );
- };
-
- useEffect(() => {
- handleRefresh();
- }, []);
-
- return useMemo(
- () => ({
- loading,
- connections: filter ? connections.filter((cs) =>
!filter.includes(cs.unique)) : connections,
- onRefresh: handleRefresh,
- onTest: handleTest,
- }),
- [loading, connections],
- );
-};
diff --git a/config-ui/src/store/tips/context.tsx
b/config-ui/src/store/tips/context.tsx
index 51e9ffef1..de0203f0b 100644
--- a/config-ui/src/store/tips/context.tsx
+++ b/config-ui/src/store/tips/context.tsx
@@ -16,31 +16,27 @@
*
*/
-import React, { useState, useContext } from 'react';
+import React, { useState } from 'react';
-const TipsContext = React.createContext<{
- text: React.ReactNode;
- setText: React.Dispatch<React.SetStateAction<React.ReactNode>>;
+export const TipsContext = React.createContext<{
+ tips: React.ReactNode;
+ setTips: React.Dispatch<React.SetStateAction<React.ReactNode>>;
}>({
- text: '',
- setText: () => {},
+ tips: '',
+ setTips: () => {},
});
export const TipsContextProvider = ({ children }: { children: React.ReactNode
}) => {
- const [text, setText] = useState<React.ReactNode>();
+ const [tips, setTips] = useState<React.ReactNode>();
return (
<TipsContext.Provider
value={{
- text,
- setText,
+ tips,
+ setTips,
}}
>
{children}
</TipsContext.Provider>
);
};
-
-export const TipsContextConsumer = TipsContext.Consumer;
-
-export const useTips = () => useContext(TipsContext);