mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-13 17:58:36 +00:00
parent
f4e6d08812
commit
1e76aae76b
@ -61,6 +61,10 @@ export const fetchSandboxConfig = (): Promise<AxiosResponse> => {
|
||||
return APIClient.get('/config/sandbox');
|
||||
};
|
||||
|
||||
export const fetchAirflowConfig = (): Promise<AxiosResponse> => {
|
||||
return APIClient.get('/config/airflow');
|
||||
};
|
||||
|
||||
export const getSuggestions: Function = (
|
||||
queryString: string,
|
||||
searchIndex?: string
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
} from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import { isEven } from '../../utils/CommonUtils';
|
||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
import { showInfoToast } from '../../utils/ToastUtils';
|
||||
import AddIngestion from '../AddIngestion/AddIngestion.component';
|
||||
import { Button } from '../buttons/Button/Button';
|
||||
@ -41,6 +42,7 @@ import EntityDeleteModal from '../Modals/EntityDeleteModal/EntityDeleteModal';
|
||||
import { IngestionProps, ModifiedConfig } from './ingestion.interface';
|
||||
|
||||
const Ingestion: React.FC<IngestionProps> = ({
|
||||
airflowEndpoint,
|
||||
serviceDetails,
|
||||
serviceName,
|
||||
serviceCategory,
|
||||
@ -270,6 +272,7 @@ const Ingestion: React.FC<IngestionProps> = ({
|
||||
<th className="tableHead-cell">Type</th>
|
||||
<th className="tableHead-cell">Schedule</th>
|
||||
<th className="tableHead-cell">Recent Runs</th>
|
||||
<th className="tableHead-cell">Airflow DAG</th>
|
||||
<th className="tableHead-cell">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -311,7 +314,30 @@ const Ingestion: React.FC<IngestionProps> = ({
|
||||
<td className="tableBody-cell">
|
||||
<div className="tw-flex">{getStatuses(ingestion)}</div>
|
||||
</td>
|
||||
|
||||
<td className="tableBody-cell">
|
||||
{airflowEndpoint ? (
|
||||
<NonAdminAction
|
||||
position="bottom"
|
||||
title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<a
|
||||
className="link-text tw-mr-2"
|
||||
data-testid="airflow-tree-view"
|
||||
href={`${airflowEndpoint}/tree?dag_id=${ingestion.name}`}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank">
|
||||
View
|
||||
<SVGIcons
|
||||
alt="external-link"
|
||||
className="tw-align-middle tw-ml-1"
|
||||
icon={Icons.EXTERNAL_LINK}
|
||||
width="12px"
|
||||
/>
|
||||
</a>
|
||||
</NonAdminAction>
|
||||
) : (
|
||||
<span className="tw-text-grey-muted">No endpoint</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="tableBody-cell">
|
||||
<NonAdminAction
|
||||
position="bottom"
|
||||
|
@ -92,6 +92,7 @@ describe('Test Ingestion page', () => {
|
||||
<Ingestion
|
||||
isRequiredDetailsAvailable
|
||||
addIngestion={mockFunction}
|
||||
airflowEndpoint=""
|
||||
currrentPage={1}
|
||||
deleteIngestion={mockDeleteIngestion}
|
||||
ingestionList={
|
||||
@ -132,6 +133,7 @@ describe('Test Ingestion page', () => {
|
||||
<Ingestion
|
||||
isRequiredDetailsAvailable
|
||||
addIngestion={mockFunction}
|
||||
airflowEndpoint=""
|
||||
currrentPage={1}
|
||||
deleteIngestion={mockDeleteIngestion}
|
||||
ingestionList={
|
||||
@ -163,12 +165,13 @@ describe('Test Ingestion page', () => {
|
||||
|
||||
expect(ingestionTable).toBeInTheDocument();
|
||||
expect(tableHeaderContainer).toBeInTheDocument();
|
||||
expect(tableHeaders.length).toBe(5);
|
||||
expect(tableHeaders.length).toBe(6);
|
||||
expect(tableHeaders).toStrictEqual([
|
||||
'Name',
|
||||
'Type',
|
||||
'Schedule',
|
||||
'Recent Runs',
|
||||
'Airflow DAG',
|
||||
'Actions',
|
||||
]);
|
||||
expect(runButton).toBeInTheDocument();
|
||||
@ -186,6 +189,7 @@ describe('Test Ingestion page', () => {
|
||||
<Ingestion
|
||||
isRequiredDetailsAvailable
|
||||
addIngestion={mockFunction}
|
||||
airflowEndpoint=""
|
||||
currrentPage={1}
|
||||
deleteIngestion={mockDeleteIngestion}
|
||||
ingestionList={
|
||||
@ -220,6 +224,7 @@ describe('Test Ingestion page', () => {
|
||||
<Ingestion
|
||||
isRequiredDetailsAvailable
|
||||
addIngestion={mockFunction}
|
||||
airflowEndpoint=""
|
||||
currrentPage={1}
|
||||
deleteIngestion={mockDeleteIngestion}
|
||||
ingestionList={
|
||||
@ -258,6 +263,7 @@ describe('Test Ingestion page', () => {
|
||||
<Ingestion
|
||||
isRequiredDetailsAvailable
|
||||
addIngestion={mockFunction}
|
||||
airflowEndpoint=""
|
||||
currrentPage={1}
|
||||
deleteIngestion={mockDeleteIngestion}
|
||||
ingestionList={
|
||||
@ -305,4 +311,33 @@ describe('Test Ingestion page', () => {
|
||||
|
||||
expect(ingestionModal).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Airflow DAG view button should be present if endpoint is available', async () => {
|
||||
const { container } = render(
|
||||
<Ingestion
|
||||
isRequiredDetailsAvailable
|
||||
addIngestion={mockFunction}
|
||||
airflowEndpoint="http://localhost"
|
||||
currrentPage={1}
|
||||
deleteIngestion={mockDeleteIngestion}
|
||||
ingestionList={
|
||||
mockIngestionWorkFlow.data.data as unknown as IngestionPipeline[]
|
||||
}
|
||||
paging={mockPaging}
|
||||
pagingHandler={mockPaginghandler}
|
||||
serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
|
||||
serviceDetails={mockService}
|
||||
serviceList={[]}
|
||||
triggerIngestion={mockTriggerIngestion}
|
||||
updateIngestion={mockFunction}
|
||||
/>,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const viewButton = await findByTestId(container, 'airflow-tree-view');
|
||||
|
||||
expect(viewButton).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -54,6 +54,7 @@ export interface IngestionData {
|
||||
}
|
||||
|
||||
export interface IngestionProps {
|
||||
airflowEndpoint: string;
|
||||
serviceDetails: DataObj;
|
||||
serviceName?: string;
|
||||
serviceCategory: ServiceCategory;
|
||||
|
@ -57,6 +57,8 @@ const jsonData = {
|
||||
|
||||
'deploy-ingestion-error': 'Error while deploying ingestion workflow!',
|
||||
|
||||
'fetch-airflow-config-error':
|
||||
'Error occurred while fetching airflow configs!',
|
||||
'fetch-auth-config-error': 'Error occurred while fetching auth configs!',
|
||||
'fetch-chart-error': 'Error while fetching charts!',
|
||||
'fetch-dashboard-details-error': 'Error while fetching dashboard details!',
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
triggerIngestionPipelineById,
|
||||
updateIngestionPipeline,
|
||||
} from '../../axiosAPIs/ingestionPipelineAPI';
|
||||
import { fetchAirflowConfig } from '../../axiosAPIs/miscAPI';
|
||||
import { getPipelines } from '../../axiosAPIs/pipelineAPI';
|
||||
import { getServiceByFQN, updateService } from '../../axiosAPIs/serviceAPI';
|
||||
import { getTopics } from '../../axiosAPIs/topicsAPI';
|
||||
@ -113,6 +114,7 @@ const ServicePage: FunctionComponent = () => {
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [ingestionCurrentPage, setIngestionCurrentPage] = useState(1);
|
||||
const [airflowEndpoint, setAirflowEndpoint] = useState<string>();
|
||||
|
||||
const getCountLabel = () => {
|
||||
switch (serviceName) {
|
||||
@ -234,6 +236,25 @@ const ServicePage: FunctionComponent = () => {
|
||||
.finally(() => setIsloading(false));
|
||||
};
|
||||
|
||||
const getAirflowEndpoint = () => {
|
||||
fetchAirflowConfig()
|
||||
.then((res) => {
|
||||
if (res.data?.apiEndpoint) {
|
||||
setAirflowEndpoint(res.data.apiEndpoint);
|
||||
} else {
|
||||
setAirflowEndpoint('');
|
||||
|
||||
throw jsonData['api-error-messages']['unexpected-server-response'];
|
||||
}
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
jsonData['api-error-messages']['fetch-airflow-config-error']
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const triggerIngestionById = (
|
||||
id: string,
|
||||
displayName: string
|
||||
@ -689,6 +710,7 @@ const ServicePage: FunctionComponent = () => {
|
||||
// getDatabaseServices();
|
||||
getAllIngestionWorkflows();
|
||||
}
|
||||
getAirflowEndpoint();
|
||||
}, []);
|
||||
|
||||
const onCancel = () => {
|
||||
@ -942,25 +964,29 @@ const ServicePage: FunctionComponent = () => {
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{activeTab === 2 && (
|
||||
<div data-testid="ingestion-container">
|
||||
<Ingestion
|
||||
isRequiredDetailsAvailable
|
||||
addIngestion={onAddIngestionSave}
|
||||
currrentPage={ingestionCurrentPage}
|
||||
deleteIngestion={deleteIngestionById}
|
||||
ingestionList={ingestions}
|
||||
paging={ingestionPaging}
|
||||
pagingHandler={ingestionPagingHandler}
|
||||
serviceCategory={serviceName as ServiceCategory}
|
||||
serviceDetails={serviceDetails as DataObj}
|
||||
serviceList={serviceList}
|
||||
serviceName={serviceFQN}
|
||||
triggerIngestion={triggerIngestionById}
|
||||
updateIngestion={updateIngestion}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{activeTab === 2 &&
|
||||
(isUndefined(airflowEndpoint) ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<div data-testid="ingestion-container">
|
||||
<Ingestion
|
||||
isRequiredDetailsAvailable
|
||||
addIngestion={onAddIngestionSave}
|
||||
airflowEndpoint={airflowEndpoint}
|
||||
currrentPage={ingestionCurrentPage}
|
||||
deleteIngestion={deleteIngestionById}
|
||||
ingestionList={ingestions}
|
||||
paging={ingestionPaging}
|
||||
pagingHandler={ingestionPagingHandler}
|
||||
serviceCategory={serviceName as ServiceCategory}
|
||||
serviceDetails={serviceDetails as DataObj}
|
||||
serviceList={serviceList}
|
||||
serviceName={serviceFQN}
|
||||
triggerIngestion={triggerIngestionById}
|
||||
updateIngestion={updateIngestion}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{activeTab === 3 && (isAdminUser || isAuthDisabled) && (
|
||||
<ServiceConfig
|
||||
|
Loading…
x
Reference in New Issue
Block a user