Skip to content

Commit

Permalink
fix: some bugs for scope config (#7452)
Browse files Browse the repository at this point in the history
  • Loading branch information
mintsweet committed May 10, 2024
1 parent 3973c95 commit 2cc08c7
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 83 deletions.
8 changes: 3 additions & 5 deletions config-ui/src/components/action/icon-button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
);
});
9 changes: 7 additions & 2 deletions config-ui/src/plugins/components/scope-config-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
},
);
Expand Down Expand Up @@ -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>
Expand Down
23 changes: 16 additions & 7 deletions config-ui/src/plugins/components/scope-config/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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) => {
Expand Down Expand Up @@ -83,7 +83,7 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, name

if (success) {
handleHideDialog();
onSuccess?.(id);
onSuccess?.(id, type === 'duplicate');
}
};

Expand All @@ -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
Expand Down Expand Up @@ -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>
))}
Expand Down
78 changes: 15 additions & 63 deletions config-ui/src/routes/blueprint/connection-detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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: {
Expand All @@ -75,20 +69,18 @@ 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]);

if (!ready || !data) {
return <PageLoading />;
}

const { blueprint, connection, scopes } = data;
const { blueprint, connection, scopeIds } = data;

const handleShowDataScope = () => setOpen(true);
const handleHideDataScope = () => setOpen(false);
Expand Down Expand Up @@ -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={
Expand Down Expand Up @@ -251,40 +223,20 @@ 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}>
<DataScopeSelect
plugin={connection.plugin}
connectionId={connection.id}
showWarning
initialScope={scopes}
initialScope={scopeIds.map((id) => ({ id }))}
onCancel={handleHideDataScope}
onSubmit={handleChangeDataScope}
/>
Expand Down
102 changes: 102 additions & 0 deletions config-ui/src/routes/blueprint/connection-detail/table.tsx
Original file line number Diff line number Diff line change
@@ -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}
</>
);
};
23 changes: 17 additions & 6 deletions config-ui/src/routes/connection/connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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>
Expand Down

0 comments on commit 2cc08c7

Please sign in to comment.