blob: d0c66d4bc2de3eff2c83aa40454a8a32e13af57c [file] [log] [blame]
/*
* 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 { useNavigate, useParams } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { DeleteOutlined, FormOutlined } from '@ant-design/icons';
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 { 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';
export const BlueprintConnectionDetailPage = () => {
const [version, setVersion] = useState(1);
const [open, setOpen] = useState(false);
const [operating, setOperating] = useState(false);
const { pname, bid, unique } = useParams() as { pname?: string; bid?: string; unique: string };
const navigate = useNavigate();
const [modal, contextHolder] = Modal.useModal();
const getBlueprint = async (pname?: string, bid?: string) => {
if (pname) {
const res = await API.project.get(pname);
return res.blueprint;
}
return API.blueprint.get(bid as any);
};
const [plugin, connectionId] = unique.split('-');
const { ready, data } = useRefreshData(async () => {
const [blueprint, connection] = await Promise.all([
getBlueprint(pname, bid),
API.connection.get(plugin, connectionId),
]);
return {
blueprint,
connection: {
unique,
plugin,
id: +connectionId,
name: connection.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, scopeIds } = data;
const handleShowDataScope = () => setOpen(true);
const handleHideDataScope = () => setOpen(false);
const handleRun = async (data?: { skipCollectors?: boolean; fullSync?: boolean }) => {
const [success] = await operator(() => API.blueprint.trigger(blueprint.id, data), {
setOperating,
hideToast: true,
});
if (success) {
navigate(pname ? PATHS.PROJECT(pname, { tab: 'status' }) : PATHS.BLUEPRINT(blueprint.id, 'status'));
}
};
const handleRemoveConnection = async () => {
const [success] = await operator(() =>
API.blueprint.update(blueprint.id, {
...blueprint,
connections: blueprint.connections.filter(
(cs) => !(cs.pluginName === connection.plugin && cs.connectionId === connection.id),
),
}),
);
if (success) {
modal.success({
closable: true,
centered: true,
width: 500,
title: 'Data Scope Changed',
content: 'Re-collect the data to get the project metrics updated?',
footer: (
<div style={{ marginTop: 20, textAlign: 'center' }}>
<Button type="primary" loading={operating} onClick={() => handleRun()}>
Recollect Data
</Button>
</div>
),
onCancel: () => {
navigate(
pname ? PATHS.PROJECT(pname, { tab: 'configuration' }) : PATHS.BLUEPRINT(blueprint.id, 'configuration'),
);
},
});
}
};
const handleChangeDataScope = async (scopeIds: any) => {
const [success] = await operator(
() =>
API.blueprint.update(blueprint.id, {
...blueprint,
connections: blueprint.connections.map((cs) => {
if (cs.pluginName === connection.plugin && cs.connectionId === connection.id) {
return {
...cs,
scopes: scopeIds.map((scopeId: any) => ({ scopeId })),
};
}
return cs;
}),
}),
{
hideToast: true,
},
);
if (success) {
handleHideDataScope();
modal.success({
closable: true,
centered: true,
width: 500,
title: 'Data Scope Changed',
content: 'Re-collect the data to get the project metrics updated?',
footer: (
<div style={{ marginTop: 20, textAlign: 'center' }}>
<Button type="primary" loading={operating} onClick={() => handleRun()}>
Recollect Data
</Button>
</div>
),
onCancel: () => {
setVersion(version + 1);
},
});
}
};
return (
<PageHeader
breadcrumbs={
pname
? [
{ name: 'Projects', path: PATHS.PROJECTS() },
{ name: pname, path: PATHS.PROJECT(pname) },
{ name: `Connection - ${connection.name}`, path: '' },
]
: [
{ name: 'Advanced', path: PATHS.BLUEPRINTS() },
{ name: 'Blueprints', path: PATHS.BLUEPRINTS() },
{ name: bid as any, path: PATHS.BLUEPRINT(bid as any) },
{ name: `Connection - ${connection.name}`, path: '' },
]
}
>
<Helmet>
<title>
{pname ? pname : blueprint.name} - {connection.name} - {brandName}
</title>
</Helmet>
<S.Top>
<span>
To manage the complete data scope and scope config for this connection, please{' '}
<ExternalLink link={`/connections/${connection.plugin}/${connection.id}`}>
go to the connection detail page
</ExternalLink>
.
</span>
<Popconfirm
placement="top"
title="Are you sure you want to remove the connection from this project/blueprint?"
cancelButtonProps={{
style: {
display: 'none',
},
}}
okText="Confirm"
onConfirm={handleRemoveConnection}
>
<Button type="primary" danger icon={<DeleteOutlined />}>
Remove this Connection
</Button>
</Popconfirm>
</S.Top>
<Flex vertical gap="middle">
<Flex>
<Button type="primary" icon={<FormOutlined />} onClick={handleShowDataScope}>
Manage Data Scope
</Button>
</Flex>
<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={scopeIds.map((id) => ({ id }))}
onCancel={handleHideDataScope}
onSubmit={handleChangeDataScope}
/>
</Modal>
{contextHolder}
</PageHeader>
);
};