You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by mi...@apache.org on 2024/05/10 07:52:41 UTC
(incubator-devlake) branch fix-scope-config created (now cc66f96da)
This is an automated email from the ASF dual-hosted git repository.
mintsweet pushed a change to branch fix-scope-config
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
at cc66f96da fix: some bugs for scope config
This branch includes the following new commits:
new cc66f96da fix: some bugs for scope config
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(incubator-devlake) 01/01: fix: some bugs for scope config
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
mintsweet pushed a commit to branch fix-scope-config
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
commit cc66f96da84589fde80391ec3fd3ac4f03911ae0
Author: mintsweet <0x...@gmail.com>
AuthorDate: Fri May 10 19:52:27 2024 +1200
fix: some bugs for scope config
---
.../src/components/action/icon-button/index.tsx | 8 +-
.../plugins/components/scope-config-form/index.tsx | 9 +-
.../src/plugins/components/scope-config/index.tsx | 23 +++--
.../routes/blueprint/connection-detail/index.tsx | 78 +++-------------
.../routes/blueprint/connection-detail/table.tsx | 102 +++++++++++++++++++++
config-ui/src/routes/connection/connection.tsx | 10 +-
6 files changed, 146 insertions(+), 84 deletions(-)
diff --git a/config-ui/src/components/action/icon-button/index.tsx b/config-ui/src/components/action/icon-button/index.tsx
index 04950c39b..fb63f60f7 100644
--- a/config-ui/src/components/action/icon-button/index.tsx
+++ b/config-ui/src/components/action/icon-button/index.tsx
@@ -20,16 +20,14 @@ import { forwardRef, Ref } from 'react';
import type { ButtonProps } from 'antd';
import { Tooltip, Button } from 'antd';
-interface Props extends Pick<ButtonProps, 'type'> {
- icon: React.ReactNode;
+interface Props extends Pick<ButtonProps, 'icon' | 'type' | 'size' | 'onClick'> {
helptip: string;
- onClick?: React.MouseEventHandler<HTMLElement> | undefined;
}
-export const IconButton = forwardRef(function ({ icon, helptip, type, onClick }: Props, ref?: Ref<HTMLElement>) {
+export const IconButton = forwardRef(function ({ helptip, ...props }: Props, ref?: Ref<HTMLElement>) {
return (
<Tooltip title={helptip}>
- <Button ref={ref} type={type} icon={icon} onClick={onClick} />
+ <Button ref={ref} {...props} />
</Tooltip>
);
});
diff --git a/config-ui/src/plugins/components/scope-config-form/index.tsx b/config-ui/src/plugins/components/scope-config-form/index.tsx
index d3519d1b2..2466e9bac 100644
--- a/config-ui/src/plugins/components/scope-config-form/index.tsx
+++ b/config-ui/src/plugins/components/scope-config-form/index.tsx
@@ -106,7 +106,7 @@ export const ScopeConfigForm = ({
: API.scopeConfig.update(plugin, connectionId, scopeConfigId, { name, entities, ...transformation }),
{
setOperating,
- hideToast: !!scopeConfigId,
+ hideSuccessToast: !!scopeConfigId,
formatMessage: () => 'Create scope config successful.',
},
);
@@ -138,7 +138,12 @@ export const ScopeConfigForm = ({
description="Give this Scope Config a unique name so that you can identify it in the future."
required
>
- <Input placeholder="My Scope Config 1" value={name} onChange={(e) => setName(e.target.value)} />
+ <Input
+ placeholder="My Scope Config 1"
+ maxLength={40}
+ value={name}
+ onChange={(e) => setName(e.target.value)}
+ />
</Block>
</Card>
<Card>
diff --git a/config-ui/src/plugins/components/scope-config/index.tsx b/config-ui/src/plugins/components/scope-config/index.tsx
index 3ef2afda9..a2b58db37 100644
--- a/config-ui/src/plugins/components/scope-config/index.tsx
+++ b/config-ui/src/plugins/components/scope-config/index.tsx
@@ -22,7 +22,7 @@ import { theme, Button, Modal, Flex, Space } from 'antd';
import styled from 'styled-components';
import API from '@/api';
-import { Message } from '@/components';
+import { IconButton, Message } from '@/components';
import { operator } from '@/utils';
import { PluginName } from '../plugin-name';
@@ -38,7 +38,7 @@ interface Props {
scopeName: string;
id?: ID;
name?: string;
- onSuccess?: (id?: ID) => void;
+ onSuccess?: (id?: ID, showToast?: boolean) => void;
}
export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name, onSuccess }: Props) => {
@@ -83,7 +83,7 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name
if (success) {
handleHideDialog();
- onSuccess?.(id);
+ onSuccess?.(id, type !== 'duplicate');
}
};
@@ -95,15 +95,24 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name
return (
<Wrapper>
<span>{id ? name : 'N/A'}</span>
- <Button
+ <IconButton
+ icon={<LinkOutlined />}
+ helptip="Associate Scope Config"
size="small"
type="link"
- icon={<LinkOutlined />}
onClick={() => {
setType('associate');
}}
/>
- {id && <Button size="small" type="link" icon={<EditOutlined />} onClick={handleCheckScopeConfig} />}
+ {id && (
+ <IconButton
+ icon={<EditOutlined />}
+ helptip=" Edit Scope Config"
+ type="link"
+ size="small"
+ onClick={handleCheckScopeConfig}
+ />
+ )}
{type === 'associate' && (
<Modal
open
@@ -172,7 +181,7 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name
<Message content="The change will apply to all following projects:" />
<ul style={{ margin: '15px 0 30px 30px' }}>
{relatedProjects.map((it) => (
- <li style={{ color: colorPrimary }}>
+ <li key={it.name} style={{ color: colorPrimary }}>
{it.name}: {it.scopes.map((sc) => sc.scopeName).join(',')}
</li>
))}
diff --git a/config-ui/src/routes/blueprint/connection-detail/index.tsx b/config-ui/src/routes/blueprint/connection-detail/index.tsx
index 7b3fa96b3..d0c66d4bc 100644
--- a/config-ui/src/routes/blueprint/connection-detail/index.tsx
+++ b/config-ui/src/routes/blueprint/connection-detail/index.tsx
@@ -20,15 +20,16 @@ import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { DeleteOutlined, FormOutlined } from '@ant-design/icons';
-import { Flex, Table, Popconfirm, Modal, Button } from 'antd';
+import { Flex, Popconfirm, Modal, Button } from 'antd';
import API from '@/api';
import { PageLoading, PageHeader, ExternalLink } from '@/components';
import { PATHS } from '@/config';
import { useRefreshData } from '@/hooks';
-import { ScopeConfig, DataScopeSelect, getPluginScopeId } from '@/plugins';
+import { DataScopeSelect } from '@/plugins';
import { operator } from '@/utils';
+import { BlueprintConnectionDetailTable } from './table';
import * as S from './styled';
const brandName = import.meta.env.DEVLAKE_BRAND_NAME ?? 'DevLake';
@@ -60,13 +61,6 @@ export const BlueprintConnectionDetailPage = () => {
API.connection.get(plugin, connectionId),
]);
- const scopeIds =
- blueprint.connections
- .find((cs) => cs.pluginName === plugin && cs.connectionId === +connectionId)
- ?.scopes?.map((sc: any) => sc.scopeId) ?? [];
-
- const scopes = await Promise.all(scopeIds.map((scopeId) => API.scope.get(plugin, connectionId, scopeId)));
-
return {
blueprint,
connection: {
@@ -75,12 +69,10 @@ export const BlueprintConnectionDetailPage = () => {
id: +connectionId,
name: connection.name,
},
- scopes: scopes.map((sc) => ({
- id: getPluginScopeId(plugin, sc.scope),
- name: sc.scope.fullName ?? sc.scope.name,
- scopeConfigId: sc.scopeConfig?.id,
- scopeConfigName: sc.scopeConfig?.name,
- })),
+ scopeIds:
+ blueprint.connections
+ .find((cs) => cs.pluginName === plugin && cs.connectionId === +connectionId)
+ ?.scopes?.map((sc: any) => sc.scopeId) ?? [],
};
}, [version, pname, bid]);
@@ -88,7 +80,7 @@ export const BlueprintConnectionDetailPage = () => {
return <PageLoading />;
}
- const { blueprint, connection, scopes } = data;
+ const { blueprint, connection, scopeIds } = data;
const handleShowDataScope = () => setOpen(true);
const handleHideDataScope = () => setOpen(false);
@@ -179,26 +171,6 @@ export const BlueprintConnectionDetailPage = () => {
}
};
- const handleChangeScopeConfig = () => {
- modal.success({
- closable: true,
- centered: true,
- width: 550,
- title: 'Scope Config Saved',
- content: 'Please re-transform data to apply the updated scope config.',
- footer: (
- <div style={{ marginTop: 20, textAlign: 'center' }}>
- <Button type="primary" loading={operating} onClick={() => handleRun({ skipCollectors: true })}>
- Re-transform now
- </Button>
- </div>
- ),
- onCancel: () => {
- setVersion(version + 1);
- },
- });
- };
-
return (
<PageHeader
breadcrumbs={
@@ -251,32 +223,12 @@ export const BlueprintConnectionDetailPage = () => {
Manage Data Scope
</Button>
</Flex>
- <Table
- rowKey="id"
- size="middle"
- columns={[
- {
- title: 'Data Scope',
- dataIndex: 'name',
- key: 'name',
- },
- {
- title: 'Scope Config',
- key: 'scopeConfig',
- render: (_, { id, name, scopeConfigId, scopeConfigName }) => (
- <ScopeConfig
- plugin={plugin}
- connectionId={connectionId}
- scopeId={id}
- scopeName={name}
- id={scopeConfigId}
- name={scopeConfigName}
- onSuccess={handleChangeScopeConfig}
- />
- ),
- },
- ]}
- dataSource={scopes}
+ <BlueprintConnectionDetailTable
+ plugin={plugin}
+ connectionId={connectionId}
+ scopeIds={scopeIds}
+ operating={operating}
+ onRun={handleRun}
/>
</Flex>
<Modal open={open} width={820} centered title="Manage Data Scope" footer={null} onCancel={handleHideDataScope}>
@@ -284,7 +236,7 @@ export const BlueprintConnectionDetailPage = () => {
plugin={connection.plugin}
connectionId={connection.id}
showWarning
- initialScope={scopes}
+ initialScope={scopeIds.map((id) => ({ id }))}
onCancel={handleHideDataScope}
onSubmit={handleChangeDataScope}
/>
diff --git a/config-ui/src/routes/blueprint/connection-detail/table.tsx b/config-ui/src/routes/blueprint/connection-detail/table.tsx
new file mode 100644
index 000000000..35de59ad1
--- /dev/null
+++ b/config-ui/src/routes/blueprint/connection-detail/table.tsx
@@ -0,0 +1,102 @@
+/*
+ * 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 } from 'react';
+import { Table, Modal, Button } from 'antd';
+
+import API from '@/api';
+import { useRefreshData } from '@/hooks';
+import { getPluginScopeId, ScopeConfig } from '@/plugins';
+
+interface Props {
+ plugin: string;
+ connectionId: ID;
+ scopeIds: ID[];
+ operating: boolean;
+ onRun: (params: { skipCollectors: boolean }) => void;
+}
+
+export const BlueprintConnectionDetailTable = ({ plugin, connectionId, scopeIds, operating, onRun }: Props) => {
+ const [version, setVersion] = useState(1);
+
+ const { ready, data } = useRefreshData(async () => {
+ const scopes = await Promise.all(scopeIds.map((scopeId) => API.scope.get(plugin, connectionId, scopeId)));
+ return scopes.map((sc) => ({
+ id: getPluginScopeId(plugin, sc.scope),
+ name: sc.scope.fullName ?? sc.scope.name,
+ scopeConfigId: sc.scopeConfig?.id,
+ scopeConfigName: sc.scopeConfig?.name,
+ }));
+ }, [version]);
+
+ const [modal, contextHolder] = Modal.useModal();
+
+ const handleChangeScopeConfig = () => {
+ modal.success({
+ closable: true,
+ centered: true,
+ width: 550,
+ title: 'Scope Config Saved',
+ content: 'Please re-transform data to apply the updated scope config.',
+ footer: (
+ <div style={{ marginTop: 20, textAlign: 'center' }}>
+ <Button type="primary" loading={operating} onClick={() => onRun({ skipCollectors: true })}>
+ Re-transform now
+ </Button>
+ </div>
+ ),
+ onCancel: () => {
+ setVersion(version + 1);
+ },
+ });
+ };
+
+ return (
+ <>
+ <Table
+ loading={!ready}
+ rowKey="id"
+ size="middle"
+ columns={[
+ {
+ title: 'Data Scope',
+ dataIndex: 'name',
+ key: 'name',
+ },
+ {
+ title: 'Scope Config',
+ key: 'scopeConfig',
+ render: (_, { id, name, scopeConfigId, scopeConfigName }) => (
+ <ScopeConfig
+ plugin={plugin}
+ connectionId={connectionId}
+ scopeId={id}
+ scopeName={name}
+ id={scopeConfigId}
+ name={scopeConfigName}
+ onSuccess={handleChangeScopeConfig}
+ />
+ ),
+ },
+ ]}
+ dataSource={data ?? []}
+ />
+ {contextHolder}
+ </>
+ );
+};
diff --git a/config-ui/src/routes/connection/connection.tsx b/config-ui/src/routes/connection/connection.tsx
index fbb90126f..fca635741 100644
--- a/config-ui/src/routes/connection/connection.tsx
+++ b/config-ui/src/routes/connection/connection.tsx
@@ -230,7 +230,7 @@ export const Connection = () => {
}
};
- const handleScopeConfigChange = async (scopeConfigId?: ID) => {
+ const handleScopeConfigChange = async (scopeConfigId?: ID, showToast?: boolean) => {
if (!scopeConfigId) {
setVersion(version + 1);
return;
@@ -239,7 +239,7 @@ export const Connection = () => {
const [success, res] = await operator(() => API.scopeConfig.check(plugin, scopeConfigId), { hideToast: true });
if (success) {
- if (!res.projects) {
+ if (!res.projects || !showToast) {
setVersion(version + 1);
return;
}
@@ -259,11 +259,7 @@ export const Connection = () => {
<li key={it.name} style={{ marginBottom: 10 }}>
<Space>
<span>{it.name}</span>
- <Button
- size="small"
- type="link"
- onClick={() => navigate(PATHS.PROJECT(it.name, { tab: 'status' }))}
- >
+ <Button size="small" type="link" href={PATHS.PROJECT(it.name, { tab: 'status' })} target="_blank">
Re-transform Data
</Button>
</Space>