Added new API integration for service page (#4158)

This commit is contained in:
Shailesh Parmar 2022-04-15 03:26:15 +05:30 committed by GitHub
parent e57ad12a36
commit a6424b2713
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 634 additions and 492 deletions

View File

@ -12,7 +12,9 @@
*/ */
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { Operation } from 'fast-json-patch';
import { CreateIngestionPipeline } from '../generated/api/services/ingestionPipelines/createIngestionPipeline'; import { CreateIngestionPipeline } from '../generated/api/services/ingestionPipelines/createIngestionPipeline';
import { getURLWithQueryFields } from '../utils/APIUtils';
import APIClient from './index'; import APIClient from './index';
export const addIngestionPipeline = ( export const addIngestionPipeline = (
@ -20,3 +22,45 @@ export const addIngestionPipeline = (
): Promise<AxiosResponse> => { ): Promise<AxiosResponse> => {
return APIClient.post('/services/ingestionPipelines', data); return APIClient.post('/services/ingestionPipelines', data);
}; };
export const getIngestionPipelines = (
arrQueryFields: Array<string>,
serviceFilter?: string,
paging?: string
): Promise<AxiosResponse> => {
const service = serviceFilter ? `service=${serviceFilter}` : '';
const url = `${getURLWithQueryFields(
'/services/ingestionPipelines',
arrQueryFields,
service
)}${paging ? paging : ''}`;
return APIClient.get(url);
};
export const triggerIngestionPipelineById = (
id: string
): Promise<AxiosResponse> => {
return APIClient.post(`/services/ingestionPipelines/trigger/${id}`);
};
export const deleteIngestionPipelineById = (
id: string
): Promise<AxiosResponse> => {
return APIClient.delete(`/services/ingestionPipelines/${id}?hardDelete=true`);
};
export const updateIngestionPipeline = (
id: string,
patch: Operation[]
): Promise<AxiosResponse> => {
const configOptions = {
headers: { 'Content-type': 'application/json-patch+json' },
};
return APIClient.patch(
`/services/ingestionPipelines/${id}`,
patch,
configOptions
);
};

View File

@ -11,6 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { isUndefined } from 'lodash';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { import {
INGESTION_SCHEDULER_INITIAL_VALUE, INGESTION_SCHEDULER_INITIAL_VALUE,
@ -18,55 +19,104 @@ import {
STEPS_FOR_ADD_INGESTION, STEPS_FOR_ADD_INGESTION,
} from '../../constants/ingestion.constant'; } from '../../constants/ingestion.constant';
import { FilterPatternEnum } from '../../enums/filterPattern.enum'; import { FilterPatternEnum } from '../../enums/filterPattern.enum';
import { FormSubmitType } from '../../enums/form.enum';
import { import {
ConfigClass,
CreateIngestionPipeline, CreateIngestionPipeline,
PipelineType,
} from '../../generated/api/services/ingestionPipelines/createIngestionPipeline'; } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline';
import {
FilterPattern,
IngestionPipeline,
} from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { getCurrentDate, getCurrentUserId } from '../../utils/CommonUtils'; import { getCurrentDate, getCurrentUserId } from '../../utils/CommonUtils';
import SuccessScreen from '../common/success-screen/SuccessScreen'; import SuccessScreen from '../common/success-screen/SuccessScreen';
import IngestionStepper from '../IngestionStepper/IngestionStepper.component'; import IngestionStepper from '../IngestionStepper/IngestionStepper.component';
import { AddIngestionProps, PatternType } from './addIngestion.interface'; import { AddIngestionProps } from './addIngestion.interface';
import ConfigureIngestion from './Steps/ConfigureIngestion'; import ConfigureIngestion from './Steps/ConfigureIngestion';
import ScheduleInterval from './Steps/ScheduleInterval'; import ScheduleInterval from './Steps/ScheduleInterval';
const AddIngestion = ({ const AddIngestion = ({
heading,
status,
pipelineType,
data,
serviceData, serviceData,
serviceCategory, serviceCategory,
showSuccessScreen = true,
onUpdateIngestion,
onSuccessSave,
onAddIngestionSave, onAddIngestionSave,
handleAddIngestion, handleCancelClick,
handleViewServiceClick, handleViewServiceClick,
}: AddIngestionProps) => { }: AddIngestionProps) => {
const [activeStepperStep, setActiveStepperStep] = useState(1); const [activeStepperStep, setActiveStepperStep] = useState(1);
const [ingestionName] = useState( const [ingestionName] = useState(
`${serviceData.name}_${PipelineType.Metadata}` data?.name ?? `${serviceData.name}_${pipelineType}`
); );
const [repeatFrequency, setRepeatFrequency] = useState( const [repeatFrequency, setRepeatFrequency] = useState(
INGESTION_SCHEDULER_INITIAL_VALUE data?.airflowConfig.scheduleInterval ?? INGESTION_SCHEDULER_INITIAL_VALUE
); );
const [startDate, setStartDate] = useState(getCurrentDate()); const [startDate, setStartDate] = useState(
const [endDate, setEndDate] = useState(''); data?.airflowConfig.startDate ?? getCurrentDate()
);
const [endDate, setEndDate] = useState(data?.airflowConfig?.endDate ?? '');
const [showDashboardFilter, setShowDashboardFilter] = useState(false); const [showDashboardFilter, setShowDashboardFilter] = useState(
const [showSchemaFilter, setShowSchemaFilter] = useState(false); !isUndefined(
const [showTableFilter, setShowTableFilter] = useState(false); (data?.source.sourceConfig.config as ConfigClass)?.dashboardFilterPattern
const [showTopicFilter, setShowTopicFilter] = useState(false); )
const [showChartFilter, setShowChartFilter] = useState(false); );
const [includeView, setIncludeView] = useState(false); const [showSchemaFilter, setShowSchemaFilter] = useState(
const [enableDataProfiler, setEnableDataProfiler] = useState(true); !isUndefined(
const [ingestSampleData, setIngestSampleData] = useState(true); (data?.source.sourceConfig.config as ConfigClass)?.schemaFilterPattern
)
);
const [showTableFilter, setShowTableFilter] = useState(
!isUndefined(
(data?.source.sourceConfig.config as ConfigClass)?.tableFilterPattern
)
);
const [showTopicFilter, setShowTopicFilter] = useState(
!isUndefined(
(data?.source.sourceConfig.config as ConfigClass)?.topicFilterPattern
)
);
const [showChartFilter, setShowChartFilter] = useState(
!isUndefined(
(data?.source.sourceConfig.config as ConfigClass)?.chartFilterPattern
)
);
const [includeView, setIncludeView] = useState(
(data?.source.sourceConfig.config as ConfigClass)?.includeViews ?? false
);
const [enableDataProfiler, setEnableDataProfiler] = useState(
(data?.source.sourceConfig.config as ConfigClass)?.enableDataProfiler ??
true
);
const [ingestSampleData, setIngestSampleData] = useState(
(data?.source.sourceConfig.config as ConfigClass)?.generateSampleData ??
true
);
const [dashboardFilterPattern, setDashboardFilterPattern] = const [dashboardFilterPattern, setDashboardFilterPattern] =
useState<PatternType>(INITIAL_FILTER_PATTERN); useState<FilterPattern>(
const [schemaFilterPattern, setSchemaFilterPattern] = useState<PatternType>( (data?.source.sourceConfig.config as ConfigClass)
INITIAL_FILTER_PATTERN ?.dashboardFilterPattern ?? INITIAL_FILTER_PATTERN
);
const [schemaFilterPattern, setSchemaFilterPattern] = useState<FilterPattern>(
(data?.source.sourceConfig.config as ConfigClass)?.schemaFilterPattern ??
INITIAL_FILTER_PATTERN
); );
const [tableFilterPattern, setTableFilterPattern] = useState<PatternType>( const [tableFilterPattern, setTableFilterPattern] = useState<FilterPattern>(
INITIAL_FILTER_PATTERN (data?.source.sourceConfig.config as ConfigClass)?.tableFilterPattern ??
INITIAL_FILTER_PATTERN
); );
const [topicFilterPattern, setTopicFilterPattern] = useState<PatternType>( const [topicFilterPattern, setTopicFilterPattern] = useState<FilterPattern>(
INITIAL_FILTER_PATTERN (data?.source.sourceConfig.config as ConfigClass)?.topicFilterPattern ??
INITIAL_FILTER_PATTERN
); );
const [chartFilterPattern, setChartFilterPattern] = useState<PatternType>( const [chartFilterPattern, setChartFilterPattern] = useState<FilterPattern>(
INITIAL_FILTER_PATTERN (data?.source.sourceConfig.config as ConfigClass)?.chartFilterPattern ??
INITIAL_FILTER_PATTERN
); );
const getIncludeValue = (value: Array<string>, type: FilterPatternEnum) => { const getIncludeValue = (value: Array<string>, type: FilterPatternEnum) => {
@ -74,24 +124,24 @@ const AddIngestion = ({
case FilterPatternEnum.DASHBOARD: case FilterPatternEnum.DASHBOARD:
setDashboardFilterPattern({ setDashboardFilterPattern({
...dashboardFilterPattern, ...dashboardFilterPattern,
include: value, includes: value,
}); });
break; break;
case FilterPatternEnum.SCHEMA: case FilterPatternEnum.SCHEMA:
setSchemaFilterPattern({ ...schemaFilterPattern, include: value }); setSchemaFilterPattern({ ...schemaFilterPattern, includes: value });
break; break;
case FilterPatternEnum.TABLE: case FilterPatternEnum.TABLE:
setTableFilterPattern({ ...tableFilterPattern, include: value }); setTableFilterPattern({ ...tableFilterPattern, includes: value });
break; break;
case FilterPatternEnum.TOPIC: case FilterPatternEnum.TOPIC:
setTopicFilterPattern({ ...topicFilterPattern, include: value }); setTopicFilterPattern({ ...topicFilterPattern, includes: value });
break; break;
case FilterPatternEnum.CHART: case FilterPatternEnum.CHART:
setChartFilterPattern({ ...topicFilterPattern, include: value }); setChartFilterPattern({ ...topicFilterPattern, includes: value });
break; break;
} }
@ -101,24 +151,24 @@ const AddIngestion = ({
case FilterPatternEnum.DASHBOARD: case FilterPatternEnum.DASHBOARD:
setDashboardFilterPattern({ setDashboardFilterPattern({
...dashboardFilterPattern, ...dashboardFilterPattern,
exclude: value, excludes: value,
}); });
break; break;
case FilterPatternEnum.SCHEMA: case FilterPatternEnum.SCHEMA:
setSchemaFilterPattern({ ...schemaFilterPattern, exclude: value }); setSchemaFilterPattern({ ...schemaFilterPattern, excludes: value });
break; break;
case FilterPatternEnum.TABLE: case FilterPatternEnum.TABLE:
setTableFilterPattern({ ...tableFilterPattern, exclude: value }); setTableFilterPattern({ ...tableFilterPattern, excludes: value });
break; break;
case FilterPatternEnum.TOPIC: case FilterPatternEnum.TOPIC:
setTopicFilterPattern({ ...topicFilterPattern, exclude: value }); setTopicFilterPattern({ ...topicFilterPattern, excludes: value });
break; break;
case FilterPatternEnum.CHART: case FilterPatternEnum.CHART:
setChartFilterPattern({ ...topicFilterPattern, exclude: value }); setChartFilterPattern({ ...topicFilterPattern, excludes: value });
break; break;
} }
@ -150,7 +200,7 @@ const AddIngestion = ({
}; };
const handleConfigureIngestionCancelClick = () => { const handleConfigureIngestionCancelClick = () => {
handleAddIngestion(false); handleCancelClick();
}; };
const handleConfigureIngestionNextClick = () => { const handleConfigureIngestionNextClick = () => {
@ -161,22 +211,22 @@ const AddIngestion = ({
setActiveStepperStep(1); setActiveStepperStep(1);
}; };
const getFilterPatternData = (data: PatternType) => { const getFilterPatternData = (data: FilterPattern) => {
const { include, exclude } = data; const { includes, excludes } = data;
return include.length === 0 && exclude.length === 0 return isUndefined(includes) && isUndefined(excludes)
? undefined ? undefined
: { : {
includes: include.length > 0 ? include : undefined, includes: includes && includes.length > 0 ? includes : undefined,
excludes: exclude.length > 0 ? exclude : undefined, excludes: excludes && excludes.length > 0 ? excludes : undefined,
}; };
}; };
const handleScheduleIntervalDeployClick = () => { const createNewIngestion = () => {
const ingestionDetails: CreateIngestionPipeline = { const ingestionDetails: CreateIngestionPipeline = {
airflowConfig: { airflowConfig: {
startDate: startDate as unknown as Date, startDate: startDate as unknown as Date,
endDate: startDate as unknown as Date, endDate: endDate as unknown as Date,
scheduleInterval: repeatFrequency, scheduleInterval: repeatFrequency,
forceDeploy: true, forceDeploy: true,
}, },
@ -186,7 +236,7 @@ const AddIngestion = ({
id: getCurrentUserId(), id: getCurrentUserId(),
type: 'user', type: 'user',
}, },
pipelineType: PipelineType.Metadata, pipelineType: pipelineType,
service: { service: {
id: serviceData.id as string, id: serviceData.id as string,
type: serviceCategory.slice(0, -1), type: serviceCategory.slice(0, -1),
@ -206,13 +256,64 @@ const AddIngestion = ({
}; };
onAddIngestionSave(ingestionDetails).then(() => { onAddIngestionSave(ingestionDetails).then(() => {
setActiveStepperStep(3); if (showSuccessScreen) {
setActiveStepperStep(3);
} else {
onSuccessSave?.();
}
}); });
}; };
const updateIngestion = () => {
if (data) {
const updatedData: IngestionPipeline = {
...data,
airflowConfig: {
...data.airflowConfig,
startDate: startDate as unknown as Date,
endDate: endDate as unknown as Date,
scheduleInterval: repeatFrequency,
},
source: {
...data.source,
sourceConfig: {
config: {
...(data.source.sourceConfig.config as ConfigClass),
enableDataProfiler: enableDataProfiler,
generateSampleData: ingestSampleData,
includeViews: includeView,
schemaFilterPattern: getFilterPatternData(schemaFilterPattern),
tableFilterPattern: getFilterPatternData(tableFilterPattern),
chartFilterPattern: getFilterPatternData(chartFilterPattern),
dashboardFilterPattern: getFilterPatternData(
dashboardFilterPattern
),
topicFilterPattern: getFilterPatternData(topicFilterPattern),
},
},
},
};
onUpdateIngestion &&
onUpdateIngestion(updatedData, data, data.id as string, data.name).then(
() => {
onSuccessSave?.();
}
);
}
};
const handleScheduleIntervalDeployClick = () => {
if (status === FormSubmitType.ADD) {
createNewIngestion();
} else {
updateIngestion();
}
};
return ( return (
<div data-testid="add-ingestion-container"> <div data-testid="add-ingestion-container">
<h6 className="tw-heading tw-text-base">Add New Ingestion</h6> <h6 className="tw-heading tw-text-base">{heading}</h6>
<IngestionStepper <IngestionStepper
activeStep={activeStepperStep} activeStep={activeStepperStep}
@ -254,20 +355,20 @@ const AddIngestion = ({
{activeStepperStep === 2 && ( {activeStepperStep === 2 && (
<ScheduleInterval <ScheduleInterval
endDate={endDate} endDate={endDate as string}
handleEndDateChange={(value: string) => setEndDate(value)} handleEndDateChange={(value: string) => setEndDate(value)}
handleRepeatFrequencyChange={(value: string) => handleRepeatFrequencyChange={(value: string) =>
setRepeatFrequency(value) setRepeatFrequency(value)
} }
handleStartDateChange={(value: string) => setStartDate(value)} handleStartDateChange={(value: string) => setStartDate(value)}
repeatFrequency={repeatFrequency} repeatFrequency={repeatFrequency}
startDate={startDate} startDate={startDate as string}
onBack={handleScheduleIntervalBackClick} onBack={handleScheduleIntervalBackClick}
onDeloy={handleScheduleIntervalDeployClick} onDeloy={handleScheduleIntervalDeployClick}
/> />
)} )}
{activeStepperStep > 2 && ( {activeStepperStep > 2 && handleViewServiceClick && (
<SuccessScreen <SuccessScreen
handleViewServiceClick={handleViewServiceClick} handleViewServiceClick={handleViewServiceClick}
name={ingestionName} name={ingestionName}

View File

@ -13,7 +13,9 @@
import { findByTestId, findByText, render } from '@testing-library/react'; import { findByTestId, findByText, render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { FormSubmitType } from '../../enums/form.enum';
import { ServiceCategory } from '../../enums/service.enum'; import { ServiceCategory } from '../../enums/service.enum';
import { PipelineType } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { DataObj } from '../../interface/service.interface'; import { DataObj } from '../../interface/service.interface';
import AddIngestion from './AddIngestion.component'; import AddIngestion from './AddIngestion.component';
import { AddIngestionProps } from './addIngestion.interface'; import { AddIngestionProps } from './addIngestion.interface';
@ -22,10 +24,13 @@ const mockAddIngestionProps: AddIngestionProps = {
serviceData: { serviceData: {
name: 'serviceName', name: 'serviceName',
} as DataObj, } as DataObj,
handleAddIngestion: jest.fn(), handleCancelClick: jest.fn(),
serviceCategory: ServiceCategory.DASHBOARD_SERVICES, serviceCategory: ServiceCategory.DASHBOARD_SERVICES,
onAddIngestionSave: jest.fn(), onAddIngestionSave: jest.fn(),
handleViewServiceClick: jest.fn(), handleViewServiceClick: jest.fn(),
pipelineType: PipelineType.Metadata,
heading: 'add ingestion',
status: FormSubmitType.ADD,
}; };
jest.mock('./Steps/ConfigureIngestion', () => { jest.mock('./Steps/ConfigureIngestion', () => {

View File

@ -30,24 +30,24 @@ jest.mock('../../common/toggle-switch/ToggleSwitchV1', () => {
const mockConfigureIngestion: ConfigureIngestionProps = { const mockConfigureIngestion: ConfigureIngestionProps = {
ingestionName: '', ingestionName: '',
dashboardFilterPattern: { dashboardFilterPattern: {
include: [], includes: [],
exclude: [], excludes: [],
}, },
chartFilterPattern: { chartFilterPattern: {
include: [], includes: [],
exclude: [], excludes: [],
}, },
schemaFilterPattern: { schemaFilterPattern: {
include: [], includes: [],
exclude: [], excludes: [],
}, },
tableFilterPattern: { tableFilterPattern: {
include: [], includes: [],
exclude: [], excludes: [],
}, },
topicFilterPattern: { topicFilterPattern: {
include: [], includes: [],
exclude: [], excludes: [],
}, },
includeView: false, includeView: false,
enableDataProfiler: false, enableDataProfiler: false,

View File

@ -52,24 +52,24 @@ const ConfigureIngestion = ({
<Fragment> <Fragment>
<FilterPattern <FilterPattern
checked={showSchemaFilter} checked={showSchemaFilter}
excludePattern={schemaFilterPattern.exclude} excludePattern={schemaFilterPattern?.excludes ?? []}
getExcludeValue={getExcludeValue} getExcludeValue={getExcludeValue}
getIncludeValue={getIncludeValue} getIncludeValue={getIncludeValue}
handleChecked={(value) => handleChecked={(value) =>
handleShowFilter(value, FilterPatternEnum.SCHEMA) handleShowFilter(value, FilterPatternEnum.SCHEMA)
} }
includePattern={schemaFilterPattern.include} includePattern={schemaFilterPattern?.includes ?? []}
type={FilterPatternEnum.SCHEMA} type={FilterPatternEnum.SCHEMA}
/> />
<FilterPattern <FilterPattern
checked={showTableFilter} checked={showTableFilter}
excludePattern={tableFilterPattern.exclude} excludePattern={tableFilterPattern?.excludes ?? []}
getExcludeValue={getExcludeValue} getExcludeValue={getExcludeValue}
getIncludeValue={getIncludeValue} getIncludeValue={getIncludeValue}
handleChecked={(value) => handleChecked={(value) =>
handleShowFilter(value, FilterPatternEnum.TABLE) handleShowFilter(value, FilterPatternEnum.TABLE)
} }
includePattern={tableFilterPattern.include} includePattern={tableFilterPattern?.includes ?? []}
showSeparator={false} showSeparator={false}
type={FilterPatternEnum.TABLE} type={FilterPatternEnum.TABLE}
/> />
@ -80,24 +80,24 @@ const ConfigureIngestion = ({
<Fragment> <Fragment>
<FilterPattern <FilterPattern
checked={showDashboardFilter} checked={showDashboardFilter}
excludePattern={dashboardFilterPattern.exclude} excludePattern={dashboardFilterPattern.excludes ?? []}
getExcludeValue={getExcludeValue} getExcludeValue={getExcludeValue}
getIncludeValue={getIncludeValue} getIncludeValue={getIncludeValue}
handleChecked={(value) => handleChecked={(value) =>
handleShowFilter(value, FilterPatternEnum.DASHBOARD) handleShowFilter(value, FilterPatternEnum.DASHBOARD)
} }
includePattern={dashboardFilterPattern.include} includePattern={dashboardFilterPattern.includes ?? []}
type={FilterPatternEnum.DASHBOARD} type={FilterPatternEnum.DASHBOARD}
/> />
<FilterPattern <FilterPattern
checked={showChartFilter} checked={showChartFilter}
excludePattern={chartFilterPattern.exclude} excludePattern={chartFilterPattern.excludes ?? []}
getExcludeValue={getExcludeValue} getExcludeValue={getExcludeValue}
getIncludeValue={getIncludeValue} getIncludeValue={getIncludeValue}
handleChecked={(value) => handleChecked={(value) =>
handleShowFilter(value, FilterPatternEnum.CHART) handleShowFilter(value, FilterPatternEnum.CHART)
} }
includePattern={chartFilterPattern.include} includePattern={chartFilterPattern.includes ?? []}
showSeparator={false} showSeparator={false}
type={FilterPatternEnum.CHART} type={FilterPatternEnum.CHART}
/> />
@ -108,13 +108,13 @@ const ConfigureIngestion = ({
return ( return (
<FilterPattern <FilterPattern
checked={showTopicFilter} checked={showTopicFilter}
excludePattern={topicFilterPattern.exclude} excludePattern={topicFilterPattern.excludes ?? []}
getExcludeValue={getExcludeValue} getExcludeValue={getExcludeValue}
getIncludeValue={getIncludeValue} getIncludeValue={getIncludeValue}
handleChecked={(value) => handleChecked={(value) =>
handleShowFilter(value, FilterPatternEnum.TOPIC) handleShowFilter(value, FilterPatternEnum.TOPIC)
} }
includePattern={topicFilterPattern.include} includePattern={topicFilterPattern.includes ?? []}
showSeparator={false} showSeparator={false}
type={FilterPatternEnum.TOPIC} type={FilterPatternEnum.TOPIC}
/> />

View File

@ -12,31 +12,45 @@
*/ */
import { FilterPatternEnum } from '../../enums/filterPattern.enum'; import { FilterPatternEnum } from '../../enums/filterPattern.enum';
import { FormSubmitType } from '../../enums/form.enum';
import { ServiceCategory } from '../../enums/service.enum'; import { ServiceCategory } from '../../enums/service.enum';
import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline'; import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline';
import {
FilterPattern,
IngestionPipeline,
PipelineType,
} from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { DataObj } from '../../interface/service.interface'; import { DataObj } from '../../interface/service.interface';
export interface AddIngestionProps { export interface AddIngestionProps {
pipelineType: PipelineType;
heading: string;
status: FormSubmitType;
data?: IngestionPipeline;
serviceCategory: ServiceCategory; serviceCategory: ServiceCategory;
serviceData: DataObj; serviceData: DataObj;
handleAddIngestion: (value: boolean) => void; showSuccessScreen?: boolean;
handleCancelClick: () => void;
onAddIngestionSave: (ingestion: CreateIngestionPipeline) => Promise<void>; onAddIngestionSave: (ingestion: CreateIngestionPipeline) => Promise<void>;
handleViewServiceClick: () => void; onUpdateIngestion?: (
data: IngestionPipeline,
oldData: IngestionPipeline,
id: string,
displayName: string,
triggerIngestion?: boolean
) => Promise<void>;
onSuccessSave?: () => void;
handleViewServiceClick?: () => void;
} }
export type PatternType = {
include: Array<string>;
exclude: Array<string>;
};
export interface ConfigureIngestionProps { export interface ConfigureIngestionProps {
ingestionName: string; ingestionName: string;
serviceCategory: ServiceCategory; serviceCategory: ServiceCategory;
dashboardFilterPattern: PatternType; dashboardFilterPattern: FilterPattern;
schemaFilterPattern: PatternType; schemaFilterPattern: FilterPattern;
tableFilterPattern: PatternType; tableFilterPattern: FilterPattern;
topicFilterPattern: PatternType; topicFilterPattern: FilterPattern;
chartFilterPattern: PatternType; chartFilterPattern: FilterPattern;
includeView: boolean; includeView: boolean;
enableDataProfiler: boolean; enableDataProfiler: boolean;
ingestSampleData: boolean; ingestSampleData: boolean;

View File

@ -11,14 +11,16 @@
* limitations under the License. * limitations under the License.
*/ */
import { isUndefined } from 'lodash'; import { capitalize, isUndefined } from 'lodash';
import { LoadingState } from 'Models'; import { LoadingState } from 'Models';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { getServiceDetailsPath, ROUTES } from '../../constants/constants'; import { getServiceDetailsPath, ROUTES } from '../../constants/constants';
import { STEPS_FOR_ADD_SERVICE } from '../../constants/services.const'; import { STEPS_FOR_ADD_SERVICE } from '../../constants/services.const';
import { FormSubmitType } from '../../enums/form.enum';
import { PageLayoutType } from '../../enums/layout.enum'; import { PageLayoutType } from '../../enums/layout.enum';
import { ServiceCategory } from '../../enums/service.enum'; import { ServiceCategory } from '../../enums/service.enum';
import { PipelineType } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { import {
ConfigData, ConfigData,
DataObj, DataObj,
@ -248,10 +250,13 @@ const AddService = ({
<div className="tw-form-container"> <div className="tw-form-container">
{addIngestion ? ( {addIngestion ? (
<AddIngestion <AddIngestion
handleAddIngestion={handleAddIngestion} handleCancelClick={() => handleAddIngestion(false)}
handleViewServiceClick={handleViewServiceClick} handleViewServiceClick={handleViewServiceClick}
heading={`Add ${capitalize(PipelineType.Metadata)} Ingestion`}
pipelineType={PipelineType.Metadata}
serviceCategory={serviceCategory} serviceCategory={serviceCategory}
serviceData={newServiceData as DataObj} serviceData={newServiceData as DataObj}
status={FormSubmitType.ADD}
onAddIngestionSave={onAddIngestionSave} onAddIngestionSave={onAddIngestionSave}
/> />
) : ( ) : (

View File

@ -15,37 +15,36 @@ import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames'; import classNames from 'classnames';
import cronstrue from 'cronstrue'; import cronstrue from 'cronstrue';
import { capitalize, isNil, lowerCase } from 'lodash'; import { capitalize, isNil, isUndefined, lowerCase } from 'lodash';
import React, { Fragment, useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider'; import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
import { import {
PAGE_SIZE, PAGE_SIZE,
TITLE_FOR_NON_ADMIN_ACTION, TITLE_FOR_NON_ADMIN_ACTION,
} from '../../constants/constants'; } from '../../constants/constants';
import { FormSubmitType } from '../../enums/form.enum';
import { import {
AirflowPipeline, IngestionPipeline,
ConfigClass,
PipelineType, PipelineType,
} from '../../generated/operations/pipelines/airflowPipeline'; } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { useAuth } from '../../hooks/authHooks'; import { useAuth } from '../../hooks/authHooks';
import { isEven } from '../../utils/CommonUtils'; import { isEven } from '../../utils/CommonUtils';
import { getAirflowPipelineTypes } from '../../utils/ServiceUtils';
import { showInfoToast } from '../../utils/ToastUtils'; import { showInfoToast } from '../../utils/ToastUtils';
import AddIngestion from '../AddIngestion/AddIngestion.component';
import { Button } from '../buttons/Button/Button'; import { Button } from '../buttons/Button/Button';
import NextPrevious from '../common/next-previous/NextPrevious'; import NextPrevious from '../common/next-previous/NextPrevious';
import NonAdminAction from '../common/non-admin-action/NonAdminAction'; import NonAdminAction from '../common/non-admin-action/NonAdminAction';
import PopOver from '../common/popover/PopOver'; import PopOver from '../common/popover/PopOver';
import Searchbar from '../common/searchbar/Searchbar'; import Searchbar from '../common/searchbar/Searchbar';
import IngestionModal from '../IngestionModal/IngestionModal.component';
import Loader from '../Loader/Loader'; import Loader from '../Loader/Loader';
import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal'; import EntityDeleteModal from '../Modals/EntityDeleteModal/EntityDeleteModal';
import { Props } from './ingestion.interface'; import { IngestionProps, ModifiedConfig } from './ingestion.interface';
const Ingestion: React.FC<Props> = ({ const Ingestion: React.FC<IngestionProps> = ({
serviceType = '', serviceDetails,
serviceName, serviceName,
serviceCategory,
ingestionList, ingestionList,
serviceList,
isRequiredDetailsAvailable, isRequiredDetailsAvailable,
deleteIngestion, deleteIngestion,
triggerIngestion, triggerIngestion,
@ -54,42 +53,45 @@ const Ingestion: React.FC<Props> = ({
paging, paging,
pagingHandler, pagingHandler,
currrentPage, currrentPage,
}: Props) => { }: IngestionProps) => {
const { isAdminUser } = useAuth(); const { isAdminUser } = useAuth();
const { isAuthDisabled } = useAuthContext(); const { isAuthDisabled } = useAuthContext();
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
const [currTriggerId, setCurrTriggerId] = useState({ id: '', state: '' }); const [currTriggerId, setCurrTriggerId] = useState({ id: '', state: '' });
const [isAdding, setIsAdding] = useState<boolean>(false); const [showIngestionForm, setShowIngestionForm] = useState(false);
const [isUpdating, setIsUpdating] = useState<boolean>(false);
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false); const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
const [deleteSelection, setDeleteSelection] = useState({ const [deleteSelection, setDeleteSelection] = useState({
id: '', id: '',
name: '', name: '',
state: '', state: '',
}); });
const [updateSelection, setUpdateSelection] = useState({ const [updateSelection, setUpdateSelection] = useState<IngestionPipeline>();
id: '',
name: '',
state: '',
ingestion: {} as AirflowPipeline,
});
const noConnectionMsg = `${serviceName} doesn't have connection details filled in. Please add the details before scheduling an ingestion job.`; const noConnectionMsg = `${serviceName} doesn't have connection details filled in. Please add the details before scheduling an ingestion job.`;
const handleSearchAction = (searchValue: string) => { const handleSearchAction = (searchValue: string) => {
setSearchText(searchValue); setSearchText(searchValue);
}; };
const getAirflowPipelineTypeOption = (): PipelineType[] => { const getIngestionPipelineTypeOption = (): PipelineType[] => {
const types = getAirflowPipelineTypes(serviceType) || []; if (ingestionList.length > 0) {
const ingestion = ingestionList[0]?.source?.serviceConnection
?.config as ModifiedConfig;
const pipelineType = [];
ingestion?.supportsMetadataExtraction &&
pipelineType.push(PipelineType.Metadata);
ingestion?.supportsUsageExtraction &&
pipelineType.push(PipelineType.Usage);
return ingestionList.reduce((prev, curr) => { return pipelineType.reduce((prev, curr) => {
const index = prev.indexOf(curr.pipelineType); if (ingestionList.find((d) => d.pipelineType === curr)) {
if (index > -1) { return prev;
prev.splice(index, 1); } else {
} return [...prev, curr];
}
}, [] as PipelineType[]);
}
return prev; return [PipelineType.Metadata, PipelineType.Usage];
}, types);
}; };
const handleTriggerIngestion = (id: string, displayName: string) => { const handleTriggerIngestion = (id: string, displayName: string) => {
@ -111,59 +113,14 @@ const Ingestion: React.FC<Props> = ({
}); });
}; };
const handleUpdate = (ingestion: AirflowPipeline) => { const handleUpdate = (ingestion: IngestionPipeline) => {
setUpdateSelection({ setUpdateSelection(ingestion);
id: ingestion.id as string, setShowIngestionForm(true);
name: ingestion.name,
state: '',
ingestion: ingestion,
});
setIsUpdating(true);
}; };
const handleCancelUpdate = () => { const handleCancelUpdate = () => {
setUpdateSelection({ setUpdateSelection(undefined);
id: '', setShowIngestionForm(false);
name: '',
state: '',
ingestion: {} as AirflowPipeline,
});
setIsUpdating(false);
};
const handleUpdateIngestion = (
data: AirflowPipeline,
triggerIngestion?: boolean
) => {
const { pipelineConfig } = updateSelection.ingestion;
const updatedData: AirflowPipeline = {
...updateSelection.ingestion,
pipelineConfig: {
...pipelineConfig,
config: {
...(pipelineConfig.config as ConfigClass),
...(data.pipelineConfig.config as ConfigClass),
},
},
scheduleInterval: data.scheduleInterval,
};
setUpdateSelection((prev) => ({ ...prev, state: 'waiting' }));
updateIngestion(
updatedData as AirflowPipeline,
updateSelection.ingestion,
updateSelection.id,
updateSelection.name,
triggerIngestion
)
.then(() => {
setTimeout(() => {
setUpdateSelection((prev) => ({ ...prev, state: 'success' }));
handleCancelUpdate();
}, 500);
})
.catch(() => {
handleCancelUpdate();
});
}; };
const handleDelete = (id: string, displayName: string) => { const handleDelete = (id: string, displayName: string) => {
@ -190,12 +147,12 @@ const Ingestion: React.FC<Props> = ({
}; };
const handleAddIngestionClick = () => { const handleAddIngestionClick = () => {
if (!getAirflowPipelineTypeOption().length) { if (!getIngestionPipelineTypeOption().length) {
showInfoToast( showInfoToast(
`${serviceName} already has all the supported ingestion jobs added.` `${serviceName} already has all the supported ingestion jobs added.`
); );
} else { } else {
setIsAdding(true); setShowIngestionForm(true);
} }
}; };
@ -211,53 +168,59 @@ const Ingestion: React.FC<Props> = ({
: ingestionList; : ingestionList;
}, [searchText, ingestionList]); }, [searchText, ingestionList]);
const getStatuses = (ingestion: AirflowPipeline) => { /* eslint-disable max-len */
const lastFiveIngestions = ingestion.pipelineStatuses // TODO:- once api support status we need below function
?.sort((a, b) => { // const getStatuses = (ingestion: AirflowPipeline) => {
// Turn your strings into millis, and then subtract them // const lastFiveIngestions = ingestion.pipelineStatuses
// to get a value that is either negative, positive, or zero. // ?.sort((a, b) => {
const date1 = new Date(a.startDate || ''); // // Turn your strings into millis, and then subtract them
const date2 = new Date(b.startDate || ''); // // to get a value that is either negative, positive, or zero.
// const date1 = new Date(a.startDate || '');
// const date2 = new Date(b.startDate || '');
return date1.getTime() - date2.getTime(); // return date1.getTime() - date2.getTime();
}) // })
.slice(Math.max(ingestion.pipelineStatuses.length - 5, 0)); // .slice(Math.max(ingestion.pipelineStatuses.length - 5, 0));
return lastFiveIngestions?.map((r, i) => { // return lastFiveIngestions?.map((r, i) => {
return ( // return (
<PopOver // <PopOver
html={ // html={
<div className="tw-text-left"> // <div className="tw-text-left">
{r.startDate ? ( // {r.startDate ? (
<p>Start Date: {new Date(r.startDate).toUTCString()}</p> // <p>Start Date: {new Date(r.startDate).toUTCString()}</p>
) : null} // ) : null}
{r.endDate ? ( // {r.endDate ? (
<p>End Date: {new Date(r.endDate).toUTCString()}</p> // <p>End Date: {new Date(r.endDate).toUTCString()}</p>
) : null} // ) : null}
</div> // </div>
} // }
key={i} // key={i}
position="bottom" // position="bottom"
theme="light" // theme="light"
trigger="mouseenter"> // trigger="mouseenter">
{i === lastFiveIngestions.length - 1 ? ( // {i === lastFiveIngestions.length - 1 ? (
<p // <p
className={`tw-h-5 tw-w-16 tw-rounded-sm tw-bg-status-${r.state} tw-mr-1 tw-px-1 tw-text-white tw-text-center`}> // className={`tw-h-5 tw-w-16 tw-rounded-sm tw-bg-status-${r.state} tw-mr-1 tw-px-1 tw-text-white tw-text-center`}>
{capitalize(r.state)} // {capitalize(r.state)}
</p> // </p>
) : ( // ) : (
<p // <p
className={`tw-w-4 tw-h-5 tw-rounded-sm tw-bg-status-${r.state} tw-mr-1`} // className={`tw-w-4 tw-h-5 tw-rounded-sm tw-bg-status-${r.state} tw-mr-1`}
/> // />
)} // )}
</PopOver> // </PopOver>
); // );
}); // });
}; // };
return ( /* eslint-enable max-len */
<Fragment>
<div className="tw-px-4" data-testid="ingestion-container"> const getIngestionTab = () => {
return (
<div
className="tw-px-4 tw-mt-4"
data-testid="ingestion-details-container">
<div className="tw-flex"> <div className="tw-flex">
{!isRequiredDetailsAvailable && ( {!isRequiredDetailsAvailable && (
<div className="tw-rounded tw-bg-error-lite tw-text-error tw-font-medium tw-px-4 tw-py-1 tw-mb-4 tw-flex tw-items-center tw-gap-1"> <div className="tw-rounded tw-bg-error-lite tw-text-error tw-font-medium tw-px-4 tw-py-1 tw-mb-4 tw-flex tw-items-center tw-gap-1">
@ -283,10 +246,12 @@ const Ingestion: React.FC<Props> = ({
position="bottom" position="bottom"
title={TITLE_FOR_NON_ADMIN_ACTION}> title={TITLE_FOR_NON_ADMIN_ACTION}>
<Button <Button
className={classNames('tw-h-8 tw-rounded tw-mb-2', { className={classNames('tw-h-8 tw-rounded tw-mb-2')}
'tw-opacity-40': !isAdminUser && !isAuthDisabled,
})}
data-testid="add-new-ingestion-button" data-testid="add-new-ingestion-button"
disabled={
getIngestionPipelineTypeOption().length === 0 ||
(!isAdminUser && !isAuthDisabled)
}
size="small" size="small"
theme="primary" theme="primary"
variant="contained" variant="contained"
@ -308,7 +273,6 @@ const Ingestion: React.FC<Props> = ({
<th className="tableHead-cell">Type</th> <th className="tableHead-cell">Type</th>
<th className="tableHead-cell">Schedule</th> <th className="tableHead-cell">Schedule</th>
<th className="tableHead-cell">Recent Runs</th> <th className="tableHead-cell">Recent Runs</th>
{/* <th className="tableHead-cell">Next Run</th> */}
<th className="tableHead-cell">Actions</th> <th className="tableHead-cell">Actions</th>
</tr> </tr>
</thead> </thead>
@ -323,30 +287,35 @@ const Ingestion: React.FC<Props> = ({
<td className="tableBody-cell">{ingestion.name}</td> <td className="tableBody-cell">{ingestion.name}</td>
<td className="tableBody-cell">{ingestion.pipelineType}</td> <td className="tableBody-cell">{ingestion.pipelineType}</td>
<td className="tableBody-cell"> <td className="tableBody-cell">
<PopOver {ingestion.airflowConfig?.scheduleInterval ? (
html={ <PopOver
<div> html={
{cronstrue.toString( <div>
ingestion.scheduleInterval || '', {cronstrue.toString(
{ ingestion.airflowConfig.scheduleInterval || '',
use24HourTimeFormat: true, {
verbose: true, use24HourTimeFormat: true,
} verbose: true,
)} }
</div> )}
} </div>
position="bottom" }
theme="light" position="bottom"
trigger="mouseenter"> theme="light"
<span>{ingestion.scheduleInterval}</span> trigger="mouseenter">
</PopOver> <span>
{ingestion.airflowConfig.scheduleInterval ?? '--'}
</span>
</PopOver>
) : (
<span>--</span>
)}
</td> </td>
<td className="tableBody-cell"> <td className="tableBody-cell">
<div className="tw-flex">{getStatuses(ingestion)}</div> {/* TODO:- update this once api support pipeline status */}
{/* <div className="tw-flex">{getStatuses(ingestion)}</div> */}
</td> </td>
{/* <td className="tableBody-cell">
{ingestion.nextExecutionDate || '--'}
</td> */}
<td className="tableBody-cell"> <td className="tableBody-cell">
<NonAdminAction <NonAdminAction
position="bottom" position="bottom"
@ -376,15 +345,7 @@ const Ingestion: React.FC<Props> = ({
data-testid="edit" data-testid="edit"
disabled={!isRequiredDetailsAvailable} disabled={!isRequiredDetailsAvailable}
onClick={() => handleUpdate(ingestion)}> onClick={() => handleUpdate(ingestion)}>
{updateSelection.id === ingestion.id ? ( Edit
updateSelection.state === 'success' ? (
<FontAwesomeIcon icon="check" />
) : (
<Loader size="small" type="default" />
)
) : (
'Edit'
)}
</button> </button>
<button <button
className="link-text tw-mr-2" className="link-text tw-mr-2"
@ -436,68 +397,60 @@ const Ingestion: React.FC<Props> = ({
</div> </div>
)} )}
</div> </div>
{isAdding ? ( );
<IngestionModal };
addIngestion={(data, triggerIngestion) => {
setIsAdding(false); const getIngestionForm = () => {
addIngestion(data, triggerIngestion); const type = getIngestionPipelineTypeOption();
}} let heading = '';
header="Add Ingestion"
ingestionList={ingestionList} if (isUndefined(updateSelection)) {
ingestionTypes={getAirflowPipelineTypeOption()} heading = `Add ${capitalize(type[0])} Ingestion`;
name="" } else {
service={serviceName} heading = `Edit ${capitalize(updateSelection.pipelineType)} Ingestion`;
serviceList={serviceList.map((s) => ({ }
name: s.name,
serviceType: s.serviceType, return (
}))} <div className="tw-bg-white tw-pt-4 tw-w-full">
serviceType={serviceType} <div className="tw-max-w-2xl tw-mx-auto tw-pb-6">
type="" <AddIngestion
onCancel={() => setIsAdding(false)} data={updateSelection}
/> handleCancelClick={handleCancelUpdate}
) : null} heading={heading}
{isUpdating ? ( pipelineType={type[0]}
<IngestionModal serviceCategory={serviceCategory}
isUpdating serviceData={serviceDetails}
header={<span>{`Edit ${updateSelection.name}`}</span>} showSuccessScreen={false}
ingestionList={ingestionList} status={
ingestionTypes={getAirflowPipelineTypes(serviceType) || []} isUndefined(updateSelection)
selectedIngestion={updateSelection.ingestion} ? FormSubmitType.ADD
service={serviceName} : FormSubmitType.EDIT
serviceList={serviceList.map((s) => ({ }
name: s.name, onAddIngestionSave={addIngestion}
serviceType: s.serviceType, onSuccessSave={handleCancelUpdate}
}))} onUpdateIngestion={updateIngestion}
serviceType={serviceType} />
updateIngestion={(data, triggerIngestion) => { </div>
setIsUpdating(false); </div>
handleUpdateIngestion(data, triggerIngestion); );
}} };
onCancel={() => handleCancelUpdate()}
/> return (
) : null} <div data-testid="ingestion-container">
{showIngestionForm ? getIngestionForm() : getIngestionTab()}
{isConfirmationModalOpen && ( {isConfirmationModalOpen && (
<ConfirmationModal <EntityDeleteModal
bodyText={`You want to delete ingestion ${deleteSelection.name} permanently? This action cannot be reverted.`} entityName={deleteSelection.name}
cancelText="Discard" entityType="ingestion"
confirmButtonCss="tw-bg-error hover:tw-bg-error focus:tw-bg-error" loadingState={deleteSelection.state}
confirmText={
deleteSelection.state === 'waiting' ? (
<Loader size="small" type="white" />
) : deleteSelection.state === 'success' ? (
<FontAwesomeIcon icon="check" />
) : (
'Delete'
)
}
header="Are you sure?"
onCancel={handleCancelConfirmationModal} onCancel={handleCancelConfirmationModal}
onConfirm={() => onConfirm={() =>
handleDelete(deleteSelection.id, deleteSelection.name) handleDelete(deleteSelection.id, deleteSelection.name)
} }
/> />
)} )}
</Fragment> </div>
); );
}; };

View File

@ -15,51 +15,82 @@ export const mockIngestionWorkFlow = {
data: { data: {
data: [ data: [
{ {
id: '3dae41fd-0469-483b-9d48-622577f2e075', id: 'c804ec51-8fcf-4040-b830-5d967c4cbf49',
name: 'test1', name: 'test3_metadata',
displayName: 'Test1', displayName: 'test3_metadata',
pipelineType: 'metadata',
owner: { owner: {
id: '360d5fd9-ba6b-4205-a92c-8eb98286c1c5', id: 'fd96fdc7-a159-4802-84be-33c68d8b7e07',
type: 'user', type: 'user',
name: 'Aaron Johnson', name: 'anonymous',
href: 'http://localhost:8585/api/v1/users/360d5fd9-ba6b-4205-a92c-8eb98286c1c5', fullyQualifiedName: 'anonymous',
deleted: false,
href: 'http://localhost:8585/api/v1/users/fd96fdc7-a159-4802-84be-33c68d8b7e07',
}, },
fullyQualifiedName: 'bigquery.test1', fullyQualifiedName: 'test3.test3_metadata',
ingestionType: 'bigquery', source: {
tags: [], type: 'bigquery',
forceDeploy: true, serviceName: 'test3',
pauseWorkflow: false, serviceConnection: {
concurrency: 1, config: {
startDate: '2021-11-24', type: 'BigQuery',
endDate: '2022-11-25', scheme: 'bigquery',
workflowTimezone: 'UTC', hostPort: 'bigquery.googleapis.com',
retries: 1, partitionField: '_PARTITIONTIME',
retryDelay: 300, partitionQuery: 'select * from {}.{} WHERE {} = "{}" LIMIT 1000',
workflowCatchup: false, tagCategoryName: 'BigqueryPolicyTags',
scheduleInterval: '0 12 * * *', connectionOptions: {},
workflowTimeout: 60, connectionArguments: {},
connectorConfig: { enablePolicyTagImport: true,
username: 'test', partitionQueryDuration: 1,
password: 'test', supportsUsageExtraction: true,
host: 'http://localhost:3000/ingestion', supportsMetadataExtraction: true,
database: 'mysql', },
includeViews: true, },
enableDataProfiler: false, sourceConfig: {
includeFilterPattern: [], config: {
excludeFilterPattern: [], includeViews: false,
enableDataProfiler: true,
generateSampleData: true,
},
},
},
openMetadataServerConnection: {
hostPort: 'http://localhost:8585/api',
authProvider: 'no-auth',
apiVersion: 'v1',
},
airflowConfig: {
forceDeploy: true,
pausePipeline: false,
concurrency: 1,
startDate: '2022-04-14',
endDate: '2022-04-14',
pipelineTimezone: 'UTC',
retries: 3,
retryDelay: 300,
pipelineCatchup: false,
scheduleInterval: '5 * * * *',
pipelineTimeout: 60,
maxActiveRuns: 1,
workflowTimeout: 60,
workflowDefaultView: 'tree',
workflowDefaultViewOrientation: 'LR',
}, },
ingestionStatuses: [],
service: { service: {
id: 'e7e34bc7-fc12-40d6-9478-a6297cdefe7a', id: 'c68e904a-4262-4b58-84c1-8a986b4aa47d',
type: 'databaseService', type: 'databaseService',
name: 'bigquery', name: 'test3',
description: 'BigQuery service used for shopify data', fullyQualifiedName: 'test3',
href: 'http://localhost:8585/api/v1/services/databaseServices/e7e34bc7-fc12-40d6-9478-a6297cdefe7a', description: '',
deleted: false,
href: 'http://localhost:8585/api/v1/services/databaseServices/c68e904a-4262-4b58-84c1-8a986b4aa47d',
}, },
href: 'http://localhost:8585/api/ingestion/3dae41fd-0469-483b-9d48-622577f2e075', href: 'http://localhost:8585/api/v1/services/ingestionPipelines/c804ec51-8fcf-4040-b830-5d967c4cbf49',
version: 0.1, version: 0.1,
updatedAt: 1637736180218, updatedAt: 1649941364738,
updatedBy: 'anonymous', updatedBy: 'anonymous',
deleted: false,
}, },
], ],
paging: { paging: {
@ -69,25 +100,37 @@ export const mockIngestionWorkFlow = {
}; };
export const mockService = { export const mockService = {
data: { id: 'c68e904a-4262-4b58-84c1-8a986b4aa47d',
data: [ name: 'test3',
{ serviceType: 'BigQuery',
id: 'e7e34bc7-fc12-40d6-9478-a6297cdefe7a', description: '',
name: 'bigquery', connection: {
serviceType: 'BigQuery', config: {
description: 'BigQuery service used for shopify data', type: 'BigQuery',
version: 0.1, scheme: 'bigquery',
updatedAt: 1637734235276, hostPort: 'bigquery.googleapis.com',
updatedBy: 'anonymous', partitionField: '_PARTITIONTIME',
href: 'http://localhost:8585/api/v1/services/databaseServices/e7e34bc7-fc12-40d6-9478-a6297cdefe7a', partitionQuery: 'select * from {}.{} WHERE {} = "{}" LIMIT 1000',
jdbc: { tagCategoryName: 'BigqueryPolicyTags',
driverClass: 'jdbc', connectionOptions: {},
connectionUrl: 'jdbc://localhost', connectionArguments: {},
}, enablePolicyTagImport: true,
}, partitionQueryDuration: 1,
], supportsUsageExtraction: true,
paging: { supportsMetadataExtraction: true,
total: 1,
}, },
}, },
version: 0.1,
updatedAt: 1649941355557,
updatedBy: 'anonymous',
owner: {
id: 'fd96fdc7-a159-4802-84be-33c68d8b7e07',
type: 'user',
name: 'anonymous',
fullyQualifiedName: 'anonymous',
deleted: false,
href: 'http://localhost:8585/api/v1/users/fd96fdc7-a159-4802-84be-33c68d8b7e07',
},
href: 'http://localhost:8585/api/v1/services/databaseServices/c68e904a-4262-4b58-84c1-8a986b4aa47d',
deleted: false,
}; };

View File

@ -20,14 +20,15 @@ import {
} from '@testing-library/react'; } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { MemoryRouter } from 'react-router'; import { MemoryRouter } from 'react-router';
import { AirflowPipeline } from '../../generated/operations/pipelines/airflowPipeline'; import { ServiceCategory } from '../../enums/service.enum';
import { IngestionPipeline } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import Ingestion from './Ingestion.component'; import Ingestion from './Ingestion.component';
import { mockIngestionWorkFlow } from './Ingestion.mock'; import { mockIngestionWorkFlow, mockService } from './Ingestion.mock';
jest.mock('../../authentication/auth-provider/AuthProvider', () => { jest.mock('../../authentication/auth-provider/AuthProvider', () => {
return { return {
useAuthContext: jest.fn(() => ({ useAuthContext: jest.fn(() => ({
isAuthDisabled: false, isAuthDisabled: true,
isAuthenticated: true, isAuthenticated: true,
isProtectedRoute: jest.fn().mockReturnValue(true), isProtectedRoute: jest.fn().mockReturnValue(true),
isTourRoute: jest.fn().mockReturnValue(false), isTourRoute: jest.fn().mockReturnValue(false),
@ -73,16 +74,16 @@ jest.mock('../common/next-previous/NextPrevious', () => {
return jest.fn().mockImplementation(() => <div>NextPrevious</div>); return jest.fn().mockImplementation(() => <div>NextPrevious</div>);
}); });
jest.mock('../IngestionModal/IngestionModal.component', () => { jest.mock('../AddIngestion/AddIngestion.component', () => {
return jest return jest
.fn() .fn()
.mockImplementation(() => ( .mockImplementation(() => (
<div data-testid="ingestion-modal">IngestionModal</div> <div data-testid="ingestion-form">AddIngestion</div>
)); ));
}); });
jest.mock('../Modals/ConfirmationModal/ConfirmationModal', () => { jest.mock('../Modals/EntityDeleteModal/EntityDeleteModal', () => {
return jest.fn().mockImplementation(() => <div>ConfirmationModal</div>); return jest.fn().mockImplementation(() => <div>EntityDeleteModal</div>);
}); });
describe('Test Ingestion page', () => { describe('Test Ingestion page', () => {
@ -94,10 +95,12 @@ describe('Test Ingestion page', () => {
currrentPage={1} currrentPage={1}
deleteIngestion={mockDeleteIngestion} deleteIngestion={mockDeleteIngestion}
ingestionList={ ingestionList={
mockIngestionWorkFlow.data.data as unknown as AirflowPipeline[] mockIngestionWorkFlow.data.data as unknown as IngestionPipeline[]
} }
paging={mockPaging} paging={mockPaging}
pagingHandler={mockPaginghandler} pagingHandler={mockPaginghandler}
serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
serviceDetails={mockService}
serviceList={[]} serviceList={[]}
triggerIngestion={mockTriggerIngestion} triggerIngestion={mockTriggerIngestion}
updateIngestion={mockFunction} updateIngestion={mockFunction}
@ -127,15 +130,17 @@ describe('Test Ingestion page', () => {
it('Table should render necessary fields', async () => { it('Table should render necessary fields', async () => {
const { container } = render( const { container } = render(
<Ingestion <Ingestion
isRequiredDetailsAvailable
addIngestion={mockFunction} addIngestion={mockFunction}
currrentPage={1} currrentPage={1}
deleteIngestion={mockDeleteIngestion} deleteIngestion={mockDeleteIngestion}
ingestionList={ ingestionList={
mockIngestionWorkFlow.data.data as unknown as AirflowPipeline[] mockIngestionWorkFlow.data.data as unknown as IngestionPipeline[]
} }
isRequiredDetailsAvailable={false}
paging={mockPaging} paging={mockPaging}
pagingHandler={mockPaginghandler} pagingHandler={mockPaginghandler}
serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
serviceDetails={mockService}
serviceList={[]} serviceList={[]}
triggerIngestion={mockTriggerIngestion} triggerIngestion={mockTriggerIngestion}
updateIngestion={mockFunction} updateIngestion={mockFunction}
@ -179,15 +184,17 @@ describe('Test Ingestion page', () => {
}; };
const { container } = render( const { container } = render(
<Ingestion <Ingestion
isRequiredDetailsAvailable
addIngestion={mockFunction} addIngestion={mockFunction}
currrentPage={1} currrentPage={1}
deleteIngestion={mockDeleteIngestion} deleteIngestion={mockDeleteIngestion}
ingestionList={ ingestionList={
mockIngestionWorkFlow.data.data as unknown as AirflowPipeline[] mockIngestionWorkFlow.data.data as unknown as IngestionPipeline[]
} }
isRequiredDetailsAvailable={false}
paging={mockPagingAfter} paging={mockPagingAfter}
pagingHandler={mockPaginghandler} pagingHandler={mockPaginghandler}
serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
serviceDetails={mockService}
serviceList={[]} serviceList={[]}
triggerIngestion={mockTriggerIngestion} triggerIngestion={mockTriggerIngestion}
updateIngestion={mockFunction} updateIngestion={mockFunction}
@ -216,12 +223,13 @@ describe('Test Ingestion page', () => {
currrentPage={1} currrentPage={1}
deleteIngestion={mockDeleteIngestion} deleteIngestion={mockDeleteIngestion}
ingestionList={ ingestionList={
mockIngestionWorkFlow.data.data as unknown as AirflowPipeline[] mockIngestionWorkFlow.data.data as unknown as IngestionPipeline[]
} }
paging={mockPagingAfter} paging={mockPagingAfter}
pagingHandler={mockPaginghandler} pagingHandler={mockPaginghandler}
serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
serviceDetails={mockService}
serviceList={[]} serviceList={[]}
serviceType="BigQuery"
triggerIngestion={mockTriggerIngestion} triggerIngestion={mockTriggerIngestion}
updateIngestion={mockFunction} updateIngestion={mockFunction}
/>, />,
@ -234,7 +242,7 @@ describe('Test Ingestion page', () => {
const editButton = await findByTestId(container, 'edit'); const editButton = await findByTestId(container, 'edit');
fireEvent.click(editButton); fireEvent.click(editButton);
const ingestionModal = await findByTestId(container, 'ingestion-modal'); const ingestionModal = await findByTestId(container, 'ingestion-form');
expect(ingestionModal).toBeInTheDocument(); expect(ingestionModal).toBeInTheDocument();
}); });
@ -253,12 +261,13 @@ describe('Test Ingestion page', () => {
currrentPage={1} currrentPage={1}
deleteIngestion={mockDeleteIngestion} deleteIngestion={mockDeleteIngestion}
ingestionList={ ingestionList={
mockIngestionWorkFlow.data.data as unknown as AirflowPipeline[] mockIngestionWorkFlow.data.data as unknown as IngestionPipeline[]
} }
paging={mockPagingAfter} paging={mockPagingAfter}
pagingHandler={mockPaginghandler} pagingHandler={mockPaginghandler}
serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
serviceDetails={mockService}
serviceList={[]} serviceList={[]}
serviceType="BigQuery"
triggerIngestion={mockTriggerIngestion} triggerIngestion={mockTriggerIngestion}
updateIngestion={mockFunction} updateIngestion={mockFunction}
/>, />,
@ -267,17 +276,6 @@ describe('Test Ingestion page', () => {
} }
); );
// on click of add ingestion
const addIngestionButton = await findByTestId(
container,
'add-new-ingestion-button'
);
fireEvent.click(addIngestionButton);
const ingestionModal = await findByTestId(container, 'ingestion-modal');
expect(ingestionModal).toBeInTheDocument();
// on click of run button // on click of run button
await act(async () => { await act(async () => {
@ -293,7 +291,18 @@ describe('Test Ingestion page', () => {
fireEvent.click(deleteButton); fireEvent.click(deleteButton);
expect( expect(
await findByText(container, /ConfirmationModal/i) await findByText(container, /EntityDeleteModal/i)
).toBeInTheDocument(); ).toBeInTheDocument();
// on click of add ingestion
const addIngestionButton = await findByTestId(
container,
'add-new-ingestion-button'
);
fireEvent.click(addIngestionButton);
const ingestionModal = await findByTestId(container, 'ingestion-form');
expect(ingestionModal).toBeInTheDocument();
}); });
}); });

View File

@ -11,11 +11,16 @@
* limitations under the License. * limitations under the License.
*/ */
import { IngestionType } from '../../enums/service.enum'; import { IngestionType, ServiceCategory } from '../../enums/service.enum';
import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline';
import { DatabaseService } from '../../generated/entity/services/databaseService'; import { DatabaseService } from '../../generated/entity/services/databaseService';
import { AirflowPipeline } from '../../generated/operations/pipelines/airflowPipeline'; import {
Connection,
IngestionPipeline,
} from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { EntityReference } from '../../generated/type/entityReference'; import { EntityReference } from '../../generated/type/entityReference';
import { Paging } from '../../generated/type/paging'; import { Paging } from '../../generated/type/paging';
import { DataObj } from '../../interface/service.interface';
export interface ConnectorConfig { export interface ConnectorConfig {
username: string; username: string;
@ -48,23 +53,29 @@ export interface IngestionData {
endDate?: string; endDate?: string;
} }
export interface Props { export interface IngestionProps {
serviceType?: string; serviceDetails: DataObj;
serviceName?: string; serviceName?: string;
serviceCategory: ServiceCategory;
isRequiredDetailsAvailable: boolean; isRequiredDetailsAvailable: boolean;
paging: Paging; paging: Paging;
ingestionList: Array<AirflowPipeline>; ingestionList: Array<IngestionPipeline>;
serviceList: Array<DatabaseService>; serviceList: Array<DatabaseService>;
currrentPage: number; currrentPage: number;
pagingHandler: (value: string | number, activePage?: number) => void; pagingHandler: (value: string | number, activePage?: number) => void;
deleteIngestion: (id: string, displayName: string) => Promise<void>; deleteIngestion: (id: string, displayName: string) => Promise<void>;
triggerIngestion: (id: string, displayName: string) => Promise<void>; triggerIngestion: (id: string, displayName: string) => Promise<void>;
addIngestion: (data: AirflowPipeline, triggerIngestion?: boolean) => void; addIngestion: (data: CreateIngestionPipeline) => Promise<void>;
updateIngestion: ( updateIngestion: (
data: AirflowPipeline, data: IngestionPipeline,
oldData: AirflowPipeline, oldData: IngestionPipeline,
id: string, id: string,
displayName: string, displayName: string,
triggerIngestion?: boolean triggerIngestion?: boolean
) => Promise<void>; ) => Promise<void>;
} }
export interface ModifiedConfig extends Connection {
supportsMetadataExtraction: boolean;
supportsUsageExtraction: boolean;
}

View File

@ -12,7 +12,7 @@
*/ */
import { StepperStepType } from 'Models'; import { StepperStepType } from 'Models';
import { PatternType } from '../components/AddIngestion/addIngestion.interface'; import { FilterPattern } from '../generated/entity/services/ingestionPipelines/ingestionPipeline';
export const STEPS_FOR_ADD_INGESTION: Array<StepperStepType> = [ export const STEPS_FOR_ADD_INGESTION: Array<StepperStepType> = [
{ name: 'Configure Ingestion', step: 1 }, { name: 'Configure Ingestion', step: 1 },
@ -21,7 +21,7 @@ export const STEPS_FOR_ADD_INGESTION: Array<StepperStepType> = [
export const INGESTION_SCHEDULER_INITIAL_VALUE = '5 * * * *'; export const INGESTION_SCHEDULER_INITIAL_VALUE = '5 * * * *';
export const INITIAL_FILTER_PATTERN: PatternType = { export const INITIAL_FILTER_PATTERN: FilterPattern = {
include: [], includes: [],
exclude: [], excludes: [],
}; };

View File

@ -26,7 +26,7 @@ import ServicePage from './index';
jest.mock('../../authentication/auth-provider/AuthProvider', () => { jest.mock('../../authentication/auth-provider/AuthProvider', () => {
return { return {
useAuthContext: jest.fn(() => ({ useAuthContext: jest.fn(() => ({
isAuthDisabled: false, isAuthDisabled: true,
isAuthenticated: true, isAuthenticated: true,
isProtectedRoute: jest.fn().mockReturnValue(true), isProtectedRoute: jest.fn().mockReturnValue(true),
isTourRoute: jest.fn().mockReturnValue(false), isTourRoute: jest.fn().mockReturnValue(false),
@ -77,8 +77,8 @@ const mockDatabase = {
}, },
}; };
jest.mock('../../axiosAPIs/airflowPipelineAPI', () => ({ jest.mock('../../axiosAPIs/ingestionPipelineAPI', () => ({
getAirflowPipelines: jest.fn().mockImplementation(() => getIngestionPipelines: jest.fn().mockImplementation(() =>
Promise.resolve({ Promise.resolve({
data: { data: {
data: [], data: [],
@ -86,10 +86,10 @@ jest.mock('../../axiosAPIs/airflowPipelineAPI', () => ({
}, },
}) })
), ),
deleteAirflowPipelineById: jest.fn(), deleteIngestionPipelineById: jest.fn(),
addAirflowPipeline: jest.fn(), addIngestionPipeline: jest.fn(),
triggerAirflowPipelineById: jest.fn(), triggerIngestionPipelineById: jest.fn(),
updateAirflowPipeline: jest.fn(), updateIngestionPipeline: jest.fn(),
})); }));
jest.mock('../../axiosAPIs/serviceAPI', () => ({ jest.mock('../../axiosAPIs/serviceAPI', () => ({

View File

@ -25,13 +25,6 @@ import React, { Fragment, FunctionComponent, useEffect, useState } from 'react';
import { Link, useHistory, useParams } from 'react-router-dom'; import { Link, useHistory, useParams } from 'react-router-dom';
import AppState from '../../AppState'; import AppState from '../../AppState';
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider'; import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
import {
addAirflowPipeline,
deleteAirflowPipelineById,
getAirflowPipelines,
triggerAirflowPipelineById,
updateAirflowPipeline,
} from '../../axiosAPIs/airflowPipelineAPI';
import { getDashboards } from '../../axiosAPIs/dashboardAPI'; import { getDashboards } from '../../axiosAPIs/dashboardAPI';
import { getDatabases } from '../../axiosAPIs/databaseAPI'; import { getDatabases } from '../../axiosAPIs/databaseAPI';
import { import {
@ -40,6 +33,13 @@ import {
postFeedById, postFeedById,
postThread, postThread,
} from '../../axiosAPIs/feedsAPI'; } from '../../axiosAPIs/feedsAPI';
import {
addIngestionPipeline,
deleteIngestionPipelineById,
getIngestionPipelines,
triggerIngestionPipelineById,
updateIngestionPipeline,
} from '../../axiosAPIs/ingestionPipelineAPI';
import { getPipelines } from '../../axiosAPIs/pipelineAPI'; import { getPipelines } from '../../axiosAPIs/pipelineAPI';
import { getServiceByFQN, updateService } from '../../axiosAPIs/serviceAPI'; import { getServiceByFQN, updateService } from '../../axiosAPIs/serviceAPI';
import { getTopics } from '../../axiosAPIs/topicsAPI'; import { getTopics } from '../../axiosAPIs/topicsAPI';
@ -70,20 +70,17 @@ import { TabSpecificField } from '../../enums/entity.enum';
import { SearchIndex } from '../../enums/search.enum'; import { SearchIndex } from '../../enums/search.enum';
import { ServiceCategory } from '../../enums/service.enum'; import { ServiceCategory } from '../../enums/service.enum';
import { CreateThread } from '../../generated/api/feed/createThread'; import { CreateThread } from '../../generated/api/feed/createThread';
import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline';
import { Dashboard } from '../../generated/entity/data/dashboard'; import { Dashboard } from '../../generated/entity/data/dashboard';
import { Database } from '../../generated/entity/data/database'; import { Database } from '../../generated/entity/data/database';
import { Pipeline } from '../../generated/entity/data/pipeline'; import { Pipeline } from '../../generated/entity/data/pipeline';
import { Topic } from '../../generated/entity/data/topic'; import { Topic } from '../../generated/entity/data/topic';
import { DatabaseService } from '../../generated/entity/services/databaseService'; import { DatabaseService } from '../../generated/entity/services/databaseService';
import { import { IngestionPipeline } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
AirflowPipeline,
PipelineType,
Schema,
} from '../../generated/operations/pipelines/airflowPipeline';
import { EntityReference } from '../../generated/type/entityReference'; import { EntityReference } from '../../generated/type/entityReference';
import { Paging } from '../../generated/type/paging'; import { Paging } from '../../generated/type/paging';
import { useAuth } from '../../hooks/authHooks'; import { useAuth } from '../../hooks/authHooks';
import { ServiceDataObj } from '../../interface/service.interface'; import { DataObj, ServiceDataObj } from '../../interface/service.interface';
import jsonData from '../../jsons/en'; import jsonData from '../../jsons/en';
import { import {
getEntityMissingError, getEntityMissingError,
@ -101,7 +98,6 @@ import {
getCurrentServiceTab, getCurrentServiceTab,
getIsIngestionEnable, getIsIngestionEnable,
getServiceCategoryFromType, getServiceCategoryFromType,
isRequiredDetailsAvailableForIngestion,
servicePageTabs, servicePageTabs,
serviceTypeLogo, serviceTypeLogo,
} from '../../utils/ServiceUtils'; } from '../../utils/ServiceUtils';
@ -137,7 +133,7 @@ const ServicePage: FunctionComponent = () => {
const [isConnectionAvailable, setConnectionAvailable] = const [isConnectionAvailable, setConnectionAvailable] =
useState<boolean>(true); useState<boolean>(true);
const [isError, setIsError] = useState(false); const [isError, setIsError] = useState(false);
const [ingestions, setIngestions] = useState<AirflowPipeline[]>([]); const [ingestions, setIngestions] = useState<IngestionPipeline[]>([]);
const [serviceList] = useState<Array<DatabaseService>>([]); const [serviceList] = useState<Array<DatabaseService>>([]);
const [ingestionPaging, setIngestionPaging] = useState<Paging>({} as Paging); const [ingestionPaging, setIngestionPaging] = useState<Paging>({} as Paging);
const [entityThread, setEntityThread] = useState<EntityThread[]>([]); const [entityThread, setEntityThread] = useState<EntityThread[]>([]);
@ -298,22 +294,9 @@ const ServicePage: FunctionComponent = () => {
}); });
}; };
const getSchemaFromType = (type: AirflowPipeline['pipelineType']) => {
switch (type) {
case PipelineType.Metadata:
return Schema.DatabaseServiceMetadataPipeline;
case PipelineType.QueryUsage:
return Schema.DatabaseServiceQueryUsagePipeline;
default:
return;
}
};
const getAllIngestionWorkflows = (paging?: string) => { const getAllIngestionWorkflows = (paging?: string) => {
setIsloading(true); setIsloading(true);
getAirflowPipelines(['owner', 'pipelineStatuses'], serviceFQN, '', paging) getIngestionPipelines(['owner'], serviceFQN, paging)
.then((res) => { .then((res) => {
if (res.data.data) { if (res.data.data) {
setIngestions(res.data.data); setIngestions(res.data.data);
@ -339,7 +322,7 @@ const ServicePage: FunctionComponent = () => {
displayName: string displayName: string
): Promise<void> => { ): Promise<void> => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
triggerAirflowPipelineById(id) triggerIngestionPipelineById(id)
.then((res) => { .then((res) => {
if (res.data) { if (res.data) {
resolve(); resolve();
@ -367,7 +350,7 @@ const ServicePage: FunctionComponent = () => {
displayName: string displayName: string
): Promise<void> => { ): Promise<void> => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
deleteAirflowPipelineById(id) deleteIngestionPipelineById(id)
.then(() => { .then(() => {
resolve(); resolve();
getAllIngestionWorkflows(); getAllIngestionWorkflows();
@ -383,8 +366,8 @@ const ServicePage: FunctionComponent = () => {
}; };
const updateIngestion = ( const updateIngestion = (
data: AirflowPipeline, data: IngestionPipeline,
oldData: AirflowPipeline, oldData: IngestionPipeline,
id: string, id: string,
displayName: string, displayName: string,
triggerIngestion?: boolean triggerIngestion?: boolean
@ -392,7 +375,7 @@ const ServicePage: FunctionComponent = () => {
const jsonPatch = compare(oldData, data); const jsonPatch = compare(oldData, data);
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
updateAirflowPipeline(id, jsonPatch) updateIngestionPipeline(id, jsonPatch)
.then(() => { .then(() => {
resolve(); resolve();
getAllIngestionWorkflows(); getAllIngestionWorkflows();
@ -415,55 +398,33 @@ const ServicePage: FunctionComponent = () => {
}); });
}; };
const addIngestionWorkflowHandler = ( const onAddIngestionSave = (data: CreateIngestionPipeline) => {
data: AirflowPipeline, return new Promise<void>((resolve, reject) => {
triggerIngestion?: boolean return addIngestionPipeline(data)
) => { .then((res: AxiosResponse) => {
setIsloading(true); if (res.data) {
getAllIngestionWorkflows();
const ingestionData: AirflowPipeline = { resolve();
...data, } else {
pipelineConfig: { showErrorToast(
...data.pipelineConfig, jsonData['api-error-messages']['create-ingestion-error']
schema: getSchemaFromType(data.pipelineType), );
}, reject();
service: {
id: serviceDetails?.id,
type: 'databaseService',
name: data.service.name,
} as EntityReference,
};
addAirflowPipeline(ingestionData)
.then((res: AxiosResponse) => {
if (res.data) {
const { id, displayName } = res.data;
setIsloading(false);
getAllIngestionWorkflows();
if (triggerIngestion) {
triggerIngestionById(id, displayName).catch((error: AxiosError) => {
showErrorToast(
error,
`${jsonData['api-error-messages']['triggering-ingestion-error']} ${displayName}`
);
});
} }
} else { })
showErrorToast(jsonData['api-error-messages']['add-ingestion-error']); .catch((error: AxiosError) => {
} const message = getErrorText(
}) error,
.catch((error: AxiosError) => { jsonData['api-error-messages']['create-ingestion-error']
const message = getErrorText( );
error, if (message.includes('Connection refused')) {
jsonData['api-error-messages']['add-ingestion-error'] setConnectionAvailable(false);
); } else {
if (message.includes('Connection refused')) { showErrorToast(message);
setConnectionAvailable(false); }
} else { reject();
showErrorToast(message); });
} });
setIsloading(false);
});
}; };
const handleConfigUpdate = ( const handleConfigUpdate = (
@ -1208,24 +1169,20 @@ const ServicePage: FunctionComponent = () => {
)} )}
{activeTab === 3 && ( {activeTab === 3 && (
<div <div data-testid="ingestion-container">
className="tw-mt-4 tw-px-1"
data-testid="ingestion-container">
{isConnectionAvailable ? ( {isConnectionAvailable ? (
<Ingestion <Ingestion
addIngestion={addIngestionWorkflowHandler} isRequiredDetailsAvailable
addIngestion={onAddIngestionSave}
currrentPage={ingestionCurrentPage} currrentPage={ingestionCurrentPage}
deleteIngestion={deleteIngestionById} deleteIngestion={deleteIngestionById}
ingestionList={ingestions} ingestionList={ingestions}
isRequiredDetailsAvailable={isRequiredDetailsAvailableForIngestion(
serviceName as ServiceCategory,
serviceDetails as ServicesData
)}
paging={ingestionPaging} paging={ingestionPaging}
pagingHandler={ingestionPagingHandler} pagingHandler={ingestionPagingHandler}
serviceCategory={serviceName as ServiceCategory}
serviceDetails={serviceDetails as DataObj}
serviceList={serviceList} serviceList={serviceList}
serviceName={serviceFQN} serviceName={serviceFQN}
serviceType={serviceDetails?.serviceType}
triggerIngestion={triggerIngestionById} triggerIngestion={triggerIngestionById}
updateIngestion={updateIngestion} updateIngestion={updateIngestion}
/> />