This is an automated email from the ASF dual-hosted git repository.
klesh 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 2cc08c7ec fix: some bugs for scope config (#7452)
2cc08c7ec is described below
commit 2cc08c7ecb15dfdaf1fd9274badad42277eb29d5
Author: 青湛 <[email protected]>
AuthorDate: Fri May 10 21:12:36 2024 +1200
fix: some bugs for scope config (#7452)
---
.../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 | 23 +++--
6 files changed, 160 insertions(+), 83 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..61ec1dc07 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, hideToast?: 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..e6b291589 100644
--- a/config-ui/src/routes/connection/connection.tsx
+++ b/config-ui/src/routes/connection/connection.tsx
@@ -230,7 +230,18 @@ export const Connection = () => {
}
};
- const handleScopeConfigChange = async (scopeConfigId?: ID) => {
+ const handleRun = async (pname: string, blueprintId: ID, data?: {
skipCollectors?: boolean; fullSync?: boolean }) => {
+ const [success] = await operator(() => API.blueprint.trigger(blueprintId,
data), {
+ setOperating,
+ hideToast: true,
+ });
+
+ if (success) {
+ window.open(PATHS.PROJECT(pname, { tab: 'status' }));
+ }
+ };
+
+ const handleScopeConfigChange = async (scopeConfigId?: ID, hideToast?:
boolean) => {
if (!scopeConfigId) {
setVersion(version + 1);
return;
@@ -239,7 +250,7 @@ export const Connection = () => {
const [success, res] = await operator(() => API.scopeConfig.check(plugin,
scopeConfigId), { hideToast: true });
if (success) {
- if (!res.projects) {
+ if (!res.projects || hideToast) {
setVersion(version + 1);
return;
}
@@ -255,14 +266,14 @@ export const Connection = () => {
The listed projects are impacted. Please re-transform the data
to apply the updated scope config.
</div>
<ul>
- {res.projects.map((it: any) => (
- <li key={it.name} style={{ marginBottom: 10 }}>
+ {res.projects.map(({ name, blueprintId }: { name: string;
blueprintId: ID }) => (
+ <li key={name} style={{ marginBottom: 10 }}>
<Space>
- <span>{it.name}</span>
+ <span>{name}</span>
<Button
size="small"
type="link"
- onClick={() => navigate(PATHS.PROJECT(it.name, { tab:
'status' }))}
+ onClick={() => handleRun(name, blueprintId, {
skipCollectors: true })}
>
Re-transform Data
</Button>