Skip to content

Commit 2cc08c7

Browse files
authored
fix: some bugs for scope config (#7452)
1 parent 3973c95 commit 2cc08c7

File tree

6 files changed

+160
-83
lines changed

6 files changed

+160
-83
lines changed

config-ui/src/components/action/icon-button/index.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,14 @@ import { forwardRef, Ref } from 'react';
2020
import type { ButtonProps } from 'antd';
2121
import { Tooltip, Button } from 'antd';
2222

23-
interface Props extends Pick<ButtonProps, 'type'> {
24-
icon: React.ReactNode;
23+
interface Props extends Pick<ButtonProps, 'icon' | 'type' | 'size' | 'onClick'> {
2524
helptip: string;
26-
onClick?: React.MouseEventHandler<HTMLElement> | undefined;
2725
}
2826

29-
export const IconButton = forwardRef(function ({ icon, helptip, type, onClick }: Props, ref?: Ref<HTMLElement>) {
27+
export const IconButton = forwardRef(function ({ helptip, ...props }: Props, ref?: Ref<HTMLElement>) {
3028
return (
3129
<Tooltip title={helptip}>
32-
<Button ref={ref} type={type} icon={icon} onClick={onClick} />
30+
<Button ref={ref} {...props} />
3331
</Tooltip>
3432
);
3533
});

config-ui/src/plugins/components/scope-config-form/index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export const ScopeConfigForm = ({
106106
: API.scopeConfig.update(plugin, connectionId, scopeConfigId, { name, entities, ...transformation }),
107107
{
108108
setOperating,
109-
hideToast: !!scopeConfigId,
109+
hideSuccessToast: !!scopeConfigId,
110110
formatMessage: () => 'Create scope config successful.',
111111
},
112112
);
@@ -138,7 +138,12 @@ export const ScopeConfigForm = ({
138138
description="Give this Scope Config a unique name so that you can identify it in the future."
139139
required
140140
>
141-
<Input placeholder="My Scope Config 1" value={name} onChange={(e) => setName(e.target.value)} />
141+
<Input
142+
placeholder="My Scope Config 1"
143+
maxLength={40}
144+
value={name}
145+
onChange={(e) => setName(e.target.value)}
146+
/>
142147
</Block>
143148
</Card>
144149
<Card>

config-ui/src/plugins/components/scope-config/index.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { theme, Button, Modal, Flex, Space } from 'antd';
2222
import styled from 'styled-components';
2323

2424
import API from '@/api';
25-
import { Message } from '@/components';
25+
import { IconButton, Message } from '@/components';
2626
import { operator } from '@/utils';
2727

2828
import { PluginName } from '../plugin-name';
@@ -38,7 +38,7 @@ interface Props {
3838
scopeName: string;
3939
id?: ID;
4040
name?: string;
41-
onSuccess?: (id?: ID) => void;
41+
onSuccess?: (id?: ID, hideToast?: boolean) => void;
4242
}
4343

4444
export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name, onSuccess }: Props) => {
@@ -83,7 +83,7 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name
8383

8484
if (success) {
8585
handleHideDialog();
86-
onSuccess?.(id);
86+
onSuccess?.(id, type === 'duplicate');
8787
}
8888
};
8989

@@ -95,15 +95,24 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name
9595
return (
9696
<Wrapper>
9797
<span>{id ? name : 'N/A'}</span>
98-
<Button
98+
<IconButton
99+
icon={<LinkOutlined />}
100+
helptip="Associate Scope Config"
99101
size="small"
100102
type="link"
101-
icon={<LinkOutlined />}
102103
onClick={() => {
103104
setType('associate');
104105
}}
105106
/>
106-
{id && <Button size="small" type="link" icon={<EditOutlined />} onClick={handleCheckScopeConfig} />}
107+
{id && (
108+
<IconButton
109+
icon={<EditOutlined />}
110+
helptip=" Edit Scope Config"
111+
type="link"
112+
size="small"
113+
onClick={handleCheckScopeConfig}
114+
/>
115+
)}
107116
{type === 'associate' && (
108117
<Modal
109118
open
@@ -172,7 +181,7 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name
172181
<Message content="The change will apply to all following projects:" />
173182
<ul style={{ margin: '15px 0 30px 30px' }}>
174183
{relatedProjects.map((it) => (
175-
<li style={{ color: colorPrimary }}>
184+
<li key={it.name} style={{ color: colorPrimary }}>
176185
{it.name}: {it.scopes.map((sc) => sc.scopeName).join(',')}
177186
</li>
178187
))}

config-ui/src/routes/blueprint/connection-detail/index.tsx

Lines changed: 15 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ import { useState } from 'react';
2020
import { useNavigate, useParams } from 'react-router-dom';
2121
import { Helmet } from 'react-helmet';
2222
import { DeleteOutlined, FormOutlined } from '@ant-design/icons';
23-
import { Flex, Table, Popconfirm, Modal, Button } from 'antd';
23+
import { Flex, Popconfirm, Modal, Button } from 'antd';
2424

2525
import API from '@/api';
2626
import { PageLoading, PageHeader, ExternalLink } from '@/components';
2727
import { PATHS } from '@/config';
2828
import { useRefreshData } from '@/hooks';
29-
import { ScopeConfig, DataScopeSelect, getPluginScopeId } from '@/plugins';
29+
import { DataScopeSelect } from '@/plugins';
3030
import { operator } from '@/utils';
3131

32+
import { BlueprintConnectionDetailTable } from './table';
3233
import * as S from './styled';
3334

3435
const brandName = import.meta.env.DEVLAKE_BRAND_NAME ?? 'DevLake';
@@ -60,13 +61,6 @@ export const BlueprintConnectionDetailPage = () => {
6061
API.connection.get(plugin, connectionId),
6162
]);
6263

63-
const scopeIds =
64-
blueprint.connections
65-
.find((cs) => cs.pluginName === plugin && cs.connectionId === +connectionId)
66-
?.scopes?.map((sc: any) => sc.scopeId) ?? [];
67-
68-
const scopes = await Promise.all(scopeIds.map((scopeId) => API.scope.get(plugin, connectionId, scopeId)));
69-
7064
return {
7165
blueprint,
7266
connection: {
@@ -75,20 +69,18 @@ export const BlueprintConnectionDetailPage = () => {
7569
id: +connectionId,
7670
name: connection.name,
7771
},
78-
scopes: scopes.map((sc) => ({
79-
id: getPluginScopeId(plugin, sc.scope),
80-
name: sc.scope.fullName ?? sc.scope.name,
81-
scopeConfigId: sc.scopeConfig?.id,
82-
scopeConfigName: sc.scopeConfig?.name,
83-
})),
72+
scopeIds:
73+
blueprint.connections
74+
.find((cs) => cs.pluginName === plugin && cs.connectionId === +connectionId)
75+
?.scopes?.map((sc: any) => sc.scopeId) ?? [],
8476
};
8577
}, [version, pname, bid]);
8678

8779
if (!ready || !data) {
8880
return <PageLoading />;
8981
}
9082

91-
const { blueprint, connection, scopes } = data;
83+
const { blueprint, connection, scopeIds } = data;
9284

9385
const handleShowDataScope = () => setOpen(true);
9486
const handleHideDataScope = () => setOpen(false);
@@ -179,26 +171,6 @@ export const BlueprintConnectionDetailPage = () => {
179171
}
180172
};
181173

182-
const handleChangeScopeConfig = () => {
183-
modal.success({
184-
closable: true,
185-
centered: true,
186-
width: 550,
187-
title: 'Scope Config Saved',
188-
content: 'Please re-transform data to apply the updated scope config.',
189-
footer: (
190-
<div style={{ marginTop: 20, textAlign: 'center' }}>
191-
<Button type="primary" loading={operating} onClick={() => handleRun({ skipCollectors: true })}>
192-
Re-transform now
193-
</Button>
194-
</div>
195-
),
196-
onCancel: () => {
197-
setVersion(version + 1);
198-
},
199-
});
200-
};
201-
202174
return (
203175
<PageHeader
204176
breadcrumbs={
@@ -251,40 +223,20 @@ export const BlueprintConnectionDetailPage = () => {
251223
Manage Data Scope
252224
</Button>
253225
</Flex>
254-
<Table
255-
rowKey="id"
256-
size="middle"
257-
columns={[
258-
{
259-
title: 'Data Scope',
260-
dataIndex: 'name',
261-
key: 'name',
262-
},
263-
{
264-
title: 'Scope Config',
265-
key: 'scopeConfig',
266-
render: (_, { id, name, scopeConfigId, scopeConfigName }) => (
267-
<ScopeConfig
268-
plugin={plugin}
269-
connectionId={connectionId}
270-
scopeId={id}
271-
scopeName={name}
272-
id={scopeConfigId}
273-
name={scopeConfigName}
274-
onSuccess={handleChangeScopeConfig}
275-
/>
276-
),
277-
},
278-
]}
279-
dataSource={scopes}
226+
<BlueprintConnectionDetailTable
227+
plugin={plugin}
228+
connectionId={connectionId}
229+
scopeIds={scopeIds}
230+
operating={operating}
231+
onRun={handleRun}
280232
/>
281233
</Flex>
282234
<Modal open={open} width={820} centered title="Manage Data Scope" footer={null} onCancel={handleHideDataScope}>
283235
<DataScopeSelect
284236
plugin={connection.plugin}
285237
connectionId={connection.id}
286238
showWarning
287-
initialScope={scopes}
239+
initialScope={scopeIds.map((id) => ({ id }))}
288240
onCancel={handleHideDataScope}
289241
onSubmit={handleChangeDataScope}
290242
/>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
import { useState } from 'react';
20+
import { Table, Modal, Button } from 'antd';
21+
22+
import API from '@/api';
23+
import { useRefreshData } from '@/hooks';
24+
import { getPluginScopeId, ScopeConfig } from '@/plugins';
25+
26+
interface Props {
27+
plugin: string;
28+
connectionId: ID;
29+
scopeIds: ID[];
30+
operating: boolean;
31+
onRun: (params: { skipCollectors: boolean }) => void;
32+
}
33+
34+
export const BlueprintConnectionDetailTable = ({ plugin, connectionId, scopeIds, operating, onRun }: Props) => {
35+
const [version, setVersion] = useState(1);
36+
37+
const { ready, data } = useRefreshData(async () => {
38+
const scopes = await Promise.all(scopeIds.map((scopeId) => API.scope.get(plugin, connectionId, scopeId)));
39+
return scopes.map((sc) => ({
40+
id: getPluginScopeId(plugin, sc.scope),
41+
name: sc.scope.fullName ?? sc.scope.name,
42+
scopeConfigId: sc.scopeConfig?.id,
43+
scopeConfigName: sc.scopeConfig?.name,
44+
}));
45+
}, [version]);
46+
47+
const [modal, contextHolder] = Modal.useModal();
48+
49+
const handleChangeScopeConfig = () => {
50+
modal.success({
51+
closable: true,
52+
centered: true,
53+
width: 550,
54+
title: 'Scope Config Saved',
55+
content: 'Please re-transform data to apply the updated scope config.',
56+
footer: (
57+
<div style={{ marginTop: 20, textAlign: 'center' }}>
58+
<Button type="primary" loading={operating} onClick={() => onRun({ skipCollectors: true })}>
59+
Re-transform now
60+
</Button>
61+
</div>
62+
),
63+
onCancel: () => {
64+
setVersion(version + 1);
65+
},
66+
});
67+
};
68+
69+
return (
70+
<>
71+
<Table
72+
loading={!ready}
73+
rowKey="id"
74+
size="middle"
75+
columns={[
76+
{
77+
title: 'Data Scope',
78+
dataIndex: 'name',
79+
key: 'name',
80+
},
81+
{
82+
title: 'Scope Config',
83+
key: 'scopeConfig',
84+
render: (_, { id, name, scopeConfigId, scopeConfigName }) => (
85+
<ScopeConfig
86+
plugin={plugin}
87+
connectionId={connectionId}
88+
scopeId={id}
89+
scopeName={name}
90+
id={scopeConfigId}
91+
name={scopeConfigName}
92+
onSuccess={handleChangeScopeConfig}
93+
/>
94+
),
95+
},
96+
]}
97+
dataSource={data ?? []}
98+
/>
99+
{contextHolder}
100+
</>
101+
);
102+
};

config-ui/src/routes/connection/connection.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,18 @@ export const Connection = () => {
230230
}
231231
};
232232

233-
const handleScopeConfigChange = async (scopeConfigId?: ID) => {
233+
const handleRun = async (pname: string, blueprintId: ID, data?: { skipCollectors?: boolean; fullSync?: boolean }) => {
234+
const [success] = await operator(() => API.blueprint.trigger(blueprintId, data), {
235+
setOperating,
236+
hideToast: true,
237+
});
238+
239+
if (success) {
240+
window.open(PATHS.PROJECT(pname, { tab: 'status' }));
241+
}
242+
};
243+
244+
const handleScopeConfigChange = async (scopeConfigId?: ID, hideToast?: boolean) => {
234245
if (!scopeConfigId) {
235246
setVersion(version + 1);
236247
return;
@@ -239,7 +250,7 @@ export const Connection = () => {
239250
const [success, res] = await operator(() => API.scopeConfig.check(plugin, scopeConfigId), { hideToast: true });
240251

241252
if (success) {
242-
if (!res.projects) {
253+
if (!res.projects || hideToast) {
243254
setVersion(version + 1);
244255
return;
245256
}
@@ -255,14 +266,14 @@ export const Connection = () => {
255266
The listed projects are impacted. Please re-transform the data to apply the updated scope config.
256267
</div>
257268
<ul>
258-
{res.projects.map((it: any) => (
259-
<li key={it.name} style={{ marginBottom: 10 }}>
269+
{res.projects.map(({ name, blueprintId }: { name: string; blueprintId: ID }) => (
270+
<li key={name} style={{ marginBottom: 10 }}>
260271
<Space>
261-
<span>{it.name}</span>
272+
<span>{name}</span>
262273
<Button
263274
size="small"
264275
type="link"
265-
onClick={() => navigate(PATHS.PROJECT(it.name, { tab: 'status' }))}
276+
onClick={() => handleRun(name, blueprintId, { skipCollectors: true })}
266277
>
267278
Re-transform Data
268279
</Button>

0 commit comments

Comments
 (0)