mirror of
https://github.com/datahub-project/datahub.git
synced 2025-11-01 03:09:12 +00:00
feat(ui/ingest): add analytics events (#14557)
This commit is contained in:
parent
22e382d1be
commit
6044d8d298
@ -194,4 +194,17 @@ yarn type-check
|
||||
|
||||
# Run tests
|
||||
yarn test
|
||||
|
||||
# Run specific test file
|
||||
yarn test path/to/file.test.tsx --run
|
||||
```
|
||||
|
||||
## Writing Tests - Best Practices & Common Pitfalls
|
||||
|
||||
### Test Setup Essentials
|
||||
|
||||
**Always use the existing test infrastructure:**
|
||||
|
||||
- Use `TestPageContainer` from `@utils/test-utils/TestPageContainer` - it provides all necessary providers
|
||||
- Use `MockedProvider` from `@apollo/client/testing` for GraphQL components
|
||||
- Use Vitest with React Testing Library
|
||||
|
||||
@ -75,6 +75,9 @@ export enum EventType {
|
||||
CreateGlossaryEntityEvent,
|
||||
CreateDomainEvent,
|
||||
MoveDomainEvent,
|
||||
IngestionTestConnectionEvent,
|
||||
IngestionExecutionResultViewedEvent,
|
||||
IngestionSourceConfigurationImpressionEvent,
|
||||
CreateIngestionSourceEvent,
|
||||
UpdateIngestionSourceEvent,
|
||||
DeleteIngestionSourceEvent,
|
||||
@ -610,18 +613,43 @@ export interface MoveDomainEvent extends BaseEvent {
|
||||
|
||||
// Managed Ingestion Events
|
||||
|
||||
export interface IngestionTestConnectionEvent extends BaseEvent {
|
||||
type: EventType.IngestionTestConnectionEvent;
|
||||
sourceType: string;
|
||||
sourceUrn?: string;
|
||||
outcome?: string;
|
||||
}
|
||||
|
||||
export interface IngestionExecutionResultViewedEvent extends BaseEvent {
|
||||
type: EventType.IngestionExecutionResultViewedEvent;
|
||||
executionUrn: string;
|
||||
executionStatus: string;
|
||||
viewedSection: string;
|
||||
}
|
||||
|
||||
export interface IngestionSourceConfigurationImpressionEvent extends BaseEvent {
|
||||
type: EventType.IngestionSourceConfigurationImpressionEvent;
|
||||
viewedSection: 'SELECT_TEMPLATE' | 'DEFINE_RECIPE' | 'CREATE_SCHEDULE' | 'NAME_SOURCE';
|
||||
sourceType?: string;
|
||||
sourceUrn?: string;
|
||||
}
|
||||
|
||||
export interface CreateIngestionSourceEvent extends BaseEvent {
|
||||
type: EventType.CreateIngestionSourceEvent;
|
||||
sourceType: string;
|
||||
sourceUrn?: string;
|
||||
interval?: string;
|
||||
numOwners?: number;
|
||||
outcome?: string;
|
||||
}
|
||||
|
||||
export interface UpdateIngestionSourceEvent extends BaseEvent {
|
||||
type: EventType.UpdateIngestionSourceEvent;
|
||||
sourceType: string;
|
||||
sourceUrn: string;
|
||||
interval?: string;
|
||||
numOwners?: number;
|
||||
outcome?: string;
|
||||
}
|
||||
|
||||
export interface DeleteIngestionSourceEvent extends BaseEvent {
|
||||
@ -630,6 +658,8 @@ export interface DeleteIngestionSourceEvent extends BaseEvent {
|
||||
|
||||
export interface ExecuteIngestionSourceEvent extends BaseEvent {
|
||||
type: EventType.ExecuteIngestionSourceEvent;
|
||||
sourceType?: string;
|
||||
sourceUrn?: string;
|
||||
}
|
||||
|
||||
// TODO: Find a way to use this event
|
||||
@ -1216,4 +1246,7 @@ export type Event =
|
||||
| WelcomeToDataHubModalInteractEvent
|
||||
| WelcomeToDataHubModalExitEvent
|
||||
| WelcomeToDataHubModalClickViewDocumentationEvent
|
||||
| ProductTourButtonClickEvent;
|
||||
| ProductTourButtonClickEvent
|
||||
| IngestionTestConnectionEvent
|
||||
| IngestionExecutionResultViewedEvent
|
||||
| IngestionSourceConfigurationImpressionEvent;
|
||||
|
||||
@ -256,6 +256,8 @@ export const IngestionSourceList = ({ showCreateModal, setShowCreateModal }: Pro
|
||||
.then(() => {
|
||||
analytics.event({
|
||||
type: EventType.ExecuteIngestionSourceEvent,
|
||||
sourceType: focusSource?.type,
|
||||
sourceUrn: focusSource?.urn,
|
||||
});
|
||||
message.success({
|
||||
content: `Successfully submitted ingestion execution request!`,
|
||||
@ -308,6 +310,7 @@ export const IngestionSourceList = ({ showCreateModal, setShowCreateModal }: Pro
|
||||
analytics.event({
|
||||
type: EventType.UpdateIngestionSourceEvent,
|
||||
sourceType: input.type,
|
||||
sourceUrn: focusSourceUrn,
|
||||
interval: input.schedule?.interval,
|
||||
});
|
||||
message.success({
|
||||
@ -359,6 +362,7 @@ export const IngestionSourceList = ({ showCreateModal, setShowCreateModal }: Pro
|
||||
analytics.event({
|
||||
type: EventType.CreateIngestionSourceEvent,
|
||||
sourceType: input.type,
|
||||
sourceUrn: newSource.urn,
|
||||
interval: input.schedule?.interval,
|
||||
});
|
||||
message.success({
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { Icon, Modal, Pill } from '@components';
|
||||
import { message } from 'antd';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { Tab, Tabs } from '@components/components/Tabs/Tabs';
|
||||
|
||||
import analytics, { EventType } from '@app/analytics';
|
||||
import { LogsTab } from '@app/ingestV2/executions/components/LogsTab';
|
||||
import { RecipeTab } from '@app/ingestV2/executions/components/RecipeTab';
|
||||
import { SummaryTab } from '@app/ingestV2/executions/components/SummaryTab';
|
||||
@ -38,6 +39,30 @@ export const ExecutionDetailsModal = ({ urn, open, onClose }: Props) => {
|
||||
const status = getIngestionSourceStatus(result);
|
||||
const [selectedTab, setSelectedTab] = useState<TabType>(TabType.Summary);
|
||||
|
||||
const sendAnalyticsTabViewedEvent = useCallback(
|
||||
(tab: TabType) => {
|
||||
if (!result) return;
|
||||
analytics.event({
|
||||
type: EventType.IngestionExecutionResultViewedEvent,
|
||||
executionUrn: urn,
|
||||
executionStatus: status || 'UNKNOWN',
|
||||
viewedSection: tab,
|
||||
});
|
||||
},
|
||||
[result, urn, status],
|
||||
);
|
||||
|
||||
const selectTab = (tab: TabType) => {
|
||||
setSelectedTab(tab);
|
||||
sendAnalyticsTabViewedEvent(tab);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
sendAnalyticsTabViewedEvent(TabType.Summary);
|
||||
}
|
||||
}, [open, urn, status, sendAnalyticsTabViewedEvent]);
|
||||
|
||||
const ResultIcon = status && getExecutionRequestStatusIcon(status);
|
||||
const resultColor = status ? getExecutionRequestStatusDisplayColor(status) : 'gray';
|
||||
const titlePill = status && ResultIcon && (
|
||||
@ -106,7 +131,7 @@ export const ExecutionDetailsModal = ({ urn, open, onClose }: Props) => {
|
||||
<Tabs
|
||||
tabs={tabs}
|
||||
selectedTab={selectedTab}
|
||||
onChange={(tab) => setSelectedTab(tab as TabType)}
|
||||
onChange={(tab) => selectTab(tab as TabType)}
|
||||
getCurrentUrl={() => window.location.pathname}
|
||||
scrollToTopOnChange
|
||||
maxHeight="80vh"
|
||||
|
||||
@ -272,6 +272,8 @@ export const IngestionSourceList = ({
|
||||
|
||||
const focusSource = finalSources.find((s) => s.urn === focusSourceUrn);
|
||||
const isLastPage = totalSources <= pageSize * page;
|
||||
// this is required when the ingestion source has not been created
|
||||
const [selectedSourceType, setSelectedSourceType] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
const sources = (data?.listIngestionSources?.ingestionSources || []) as IngestionSource[];
|
||||
@ -392,8 +394,10 @@ export const IngestionSourceList = ({
|
||||
analytics.event({
|
||||
type: EventType.UpdateIngestionSourceEvent,
|
||||
sourceType: input.type,
|
||||
sourceUrn: focusSourceUrn,
|
||||
interval: input.schedule?.interval,
|
||||
numOwners: owners?.length,
|
||||
outcome: shouldRun ? 'save_and_run' : 'save',
|
||||
});
|
||||
message.success({
|
||||
content: `Successfully updated ingestion source!`,
|
||||
@ -460,8 +464,10 @@ export const IngestionSourceList = ({
|
||||
analytics.event({
|
||||
type: EventType.CreateIngestionSourceEvent,
|
||||
sourceType: input.type,
|
||||
sourceUrn: newSource.urn,
|
||||
interval: input.schedule?.interval,
|
||||
numOwners: ownersToAdd?.length,
|
||||
outcome: shouldRun ? 'save_and_run' : 'save',
|
||||
});
|
||||
message.success({
|
||||
content: `Successfully created ingestion source!`,
|
||||
@ -696,6 +702,8 @@ export const IngestionSourceList = ({
|
||||
return Promise.resolve();
|
||||
}}
|
||||
selectedSource={focusSource}
|
||||
selectedSourceType={selectedSourceType}
|
||||
setSelectedSourceType={setSelectedSourceType}
|
||||
loading={isModalWaiting}
|
||||
/>
|
||||
{isViewingRecipe && <RecipeViewerModal recipe={focusSource?.config?.recipe} onCancel={onCancel} />}
|
||||
|
||||
@ -39,7 +39,15 @@ const ControlsContainer = styled.div`
|
||||
/**
|
||||
* The step for defining a recipe
|
||||
*/
|
||||
export const DefineRecipeStep = ({ state, updateState, goTo, prev, ingestionSources }: StepProps) => {
|
||||
export const DefineRecipeStep = ({
|
||||
state,
|
||||
updateState,
|
||||
goTo,
|
||||
prev,
|
||||
ingestionSources,
|
||||
selectedSource,
|
||||
setSelectedSourceType,
|
||||
}: StepProps) => {
|
||||
const existingRecipeJson = state.config?.recipe;
|
||||
const existingRecipeYaml = existingRecipeJson && jsonToYaml(existingRecipeJson);
|
||||
const { type } = state;
|
||||
@ -96,6 +104,7 @@ export const DefineRecipeStep = ({ state, updateState, goTo, prev, ingestionSour
|
||||
updateState(newState);
|
||||
|
||||
goTo(IngestionSourceBuilderStep.CREATE_SCHEDULE);
|
||||
setSelectedSourceType?.(newState.type);
|
||||
};
|
||||
|
||||
if (type && CONNECTORS_WITH_FORM.has(type)) {
|
||||
@ -109,6 +118,7 @@ export const DefineRecipeStep = ({ state, updateState, goTo, prev, ingestionSour
|
||||
setStagedRecipe={setStagedRecipeYml}
|
||||
onClickNext={onClickNext}
|
||||
goToPrevious={prev}
|
||||
selectedSource={selectedSource}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,9 +2,10 @@ import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { Modal } from '@components';
|
||||
import { Spin, Steps } from 'antd';
|
||||
import { isEqual } from 'lodash';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import analytics, { EventType } from '@app/analytics';
|
||||
import { CreateScheduleStep } from '@app/ingestV2/source/builder/CreateScheduleStep';
|
||||
import { DefineRecipeStep } from '@app/ingestV2/source/builder/DefineRecipeStep';
|
||||
import { NameSourceStep } from '@app/ingestV2/source/builder/NameSourceStep';
|
||||
@ -58,6 +59,8 @@ type Props = {
|
||||
sourceRefetch?: () => Promise<any>;
|
||||
selectedSource?: IngestionSource;
|
||||
loading?: boolean;
|
||||
selectedSourceType?: string;
|
||||
setSelectedSourceType?: (sourceType: string) => void;
|
||||
};
|
||||
|
||||
export const IngestionSourceBuilderModal = ({
|
||||
@ -68,6 +71,8 @@ export const IngestionSourceBuilderModal = ({
|
||||
sourceRefetch,
|
||||
selectedSource,
|
||||
loading,
|
||||
selectedSourceType,
|
||||
setSelectedSourceType,
|
||||
}: Props) => {
|
||||
const isEditing = initialState !== undefined;
|
||||
const titleText = isEditing ? 'Edit Data Source' : 'Connect Data Source';
|
||||
@ -84,20 +89,46 @@ export const IngestionSourceBuilderModal = ({
|
||||
|
||||
const ingestionSources = JSON.parse(JSON.stringify(sourcesJson)); // TODO: replace with call to server once we have access to dynamic list of sources
|
||||
|
||||
// Reset the ingestion builder modal state when the modal is re-opened.
|
||||
const prevInitialState = useRef(initialState);
|
||||
useEffect(() => {
|
||||
if (!isEqual(prevInitialState.current, initialState)) {
|
||||
setIngestionBuilderState(initialState || {});
|
||||
}
|
||||
prevInitialState.current = initialState;
|
||||
}, [initialState]);
|
||||
const sendAnalyticsStepViewedEvent = useCallback(
|
||||
(step: IngestionSourceBuilderStep) => {
|
||||
if (open) {
|
||||
analytics.event({
|
||||
type: EventType.IngestionSourceConfigurationImpressionEvent,
|
||||
viewedSection: step,
|
||||
sourceType: selectedSource?.type || selectedSourceType,
|
||||
sourceUrn: selectedSource?.urn,
|
||||
});
|
||||
}
|
||||
},
|
||||
[selectedSource?.type, selectedSource?.urn, selectedSourceType, open],
|
||||
);
|
||||
|
||||
// Reset the step stack to the initial step when the modal is re-opened.
|
||||
useEffect(() => setStepStack([initialStep]), [initialStep]);
|
||||
// Reset the modal state when initialState changes or modal opens
|
||||
const prevInitialState = useRef(initialState);
|
||||
const prevOpen = useRef(open);
|
||||
useEffect(() => {
|
||||
const stateChanged = !isEqual(prevInitialState.current, initialState);
|
||||
const modalOpened = !prevOpen.current && open;
|
||||
|
||||
if (stateChanged) {
|
||||
setIngestionBuilderState(initialState || {});
|
||||
setStepStack([initialStep]);
|
||||
setSelectedSourceType?.('');
|
||||
prevInitialState.current = initialState;
|
||||
}
|
||||
|
||||
// Fire event when modal opens
|
||||
if (modalOpened) {
|
||||
setStepStack([initialStep]); // Ensure correct step when modal opens
|
||||
sendAnalyticsStepViewedEvent(initialStep);
|
||||
}
|
||||
|
||||
prevOpen.current = open;
|
||||
}, [initialState, initialStep, open, sendAnalyticsStepViewedEvent, setSelectedSourceType]);
|
||||
|
||||
const goTo = (step: IngestionSourceBuilderStep) => {
|
||||
setStepStack([...stepStack, step]);
|
||||
sendAnalyticsStepViewedEvent(step);
|
||||
};
|
||||
|
||||
const prev = () => {
|
||||
@ -151,6 +182,8 @@ export const IngestionSourceBuilderModal = ({
|
||||
ingestionSources={ingestionSources}
|
||||
sourceRefetch={sourceRefetch}
|
||||
selectedSource={selectedSource}
|
||||
selectedSourceType={selectedSourceType}
|
||||
setSelectedSourceType={setSelectedSourceType}
|
||||
/>
|
||||
</Spin>
|
||||
</Modal>
|
||||
|
||||
@ -14,6 +14,8 @@ import { CSV, LOOKER, LOOK_ML } from '@app/ingestV2/source/builder/constants';
|
||||
import { SourceBuilderState, SourceConfig } from '@app/ingestV2/source/builder/types';
|
||||
import { Button } from '@src/alchemy-components';
|
||||
|
||||
import { IngestionSource } from '@types';
|
||||
|
||||
export const ControlsContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -61,13 +63,23 @@ interface Props {
|
||||
isEditing: boolean;
|
||||
displayRecipe: string;
|
||||
sourceConfigs?: SourceConfig;
|
||||
selectedSource?: IngestionSource;
|
||||
setStagedRecipe: (recipe: string) => void;
|
||||
onClickNext: () => void;
|
||||
goToPrevious?: () => void;
|
||||
}
|
||||
|
||||
function RecipeBuilder(props: Props) {
|
||||
const { state, isEditing, displayRecipe, sourceConfigs, setStagedRecipe, onClickNext, goToPrevious } = props;
|
||||
const {
|
||||
state,
|
||||
isEditing,
|
||||
displayRecipe,
|
||||
sourceConfigs,
|
||||
setStagedRecipe,
|
||||
onClickNext,
|
||||
goToPrevious,
|
||||
selectedSource,
|
||||
} = props;
|
||||
const { type } = state;
|
||||
const [isViewingForm, setIsViewingForm] = useState(true);
|
||||
const [hideDocsHint, setHideDocsHint] = useState(false);
|
||||
@ -119,6 +131,7 @@ function RecipeBuilder(props: Props) {
|
||||
{isViewingForm && (
|
||||
<RecipeForm
|
||||
state={state}
|
||||
selectedSource={selectedSource}
|
||||
isEditing={isEditing}
|
||||
displayRecipe={displayRecipe}
|
||||
sourceConfigs={sourceConfigs}
|
||||
|
||||
@ -16,6 +16,7 @@ import { jsonToYaml } from '@app/ingestV2/source/utils';
|
||||
import { RequiredFieldForm } from '@app/shared/form/RequiredFieldForm';
|
||||
|
||||
import { useListSecretsQuery } from '@graphql/ingestion.generated';
|
||||
import { IngestionSource } from '@types';
|
||||
|
||||
export const ControlsContainer = styled.div`
|
||||
display: flex;
|
||||
@ -102,10 +103,20 @@ interface Props {
|
||||
setStagedRecipe: (recipe: string) => void;
|
||||
onClickNext: () => void;
|
||||
goToPrevious?: () => void;
|
||||
selectedSource?: IngestionSource;
|
||||
}
|
||||
|
||||
function RecipeForm(props: Props) {
|
||||
const { state, isEditing, displayRecipe, sourceConfigs, setStagedRecipe, onClickNext, goToPrevious } = props;
|
||||
const {
|
||||
state,
|
||||
isEditing,
|
||||
displayRecipe,
|
||||
sourceConfigs,
|
||||
setStagedRecipe,
|
||||
onClickNext,
|
||||
goToPrevious,
|
||||
selectedSource,
|
||||
} = props;
|
||||
const { type } = state;
|
||||
const version = state.config?.version;
|
||||
const { fields, advancedFields, filterFields, filterSectionTooltip, advancedSectionTooltip, defaultOpenSections } =
|
||||
@ -171,6 +182,7 @@ function RecipeForm(props: Props) {
|
||||
recipe={displayRecipe}
|
||||
sourceConfigs={sourceConfigs}
|
||||
version={version}
|
||||
selectedSource={selectedSource}
|
||||
/>
|
||||
</TestConnectionWrapper>
|
||||
)}
|
||||
|
||||
@ -2,6 +2,7 @@ import { CheckCircleOutlined } from '@ant-design/icons';
|
||||
import { message } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import analytics, { EventType } from '@app/analytics';
|
||||
import { EXECUTION_REQUEST_STATUS_FAILURE, EXECUTION_REQUEST_STATUS_RUNNING } from '@app/ingestV2/executions/constants';
|
||||
import TestConnectionModal from '@app/ingestV2/source/builder/RecipeForm/TestConnection/TestConnectionModal';
|
||||
import { TestConnectionResult } from '@app/ingestV2/source/builder/RecipeForm/TestConnection/types';
|
||||
@ -13,6 +14,7 @@ import {
|
||||
useCreateTestConnectionRequestMutation,
|
||||
useGetIngestionExecutionRequestLazyQuery,
|
||||
} from '@graphql/ingestion.generated';
|
||||
import { ExecutionRequestResult, IngestionSource } from '@types';
|
||||
|
||||
export function getRecipeJson(recipeYaml: string) {
|
||||
// Convert the recipe into it's json representation, and catch + report exceptions while we do it.
|
||||
@ -29,18 +31,33 @@ export function getRecipeJson(recipeYaml: string) {
|
||||
return recipeJson;
|
||||
}
|
||||
|
||||
export function getSourceTypeFromRecipeJson(recipeJson: string) {
|
||||
const recipe = JSON.parse(recipeJson);
|
||||
return recipe.source.type;
|
||||
}
|
||||
|
||||
export function getBasicConnectivityFromResult(result: ExecutionRequestResult) {
|
||||
if (!result?.structuredReport?.serializedValue) {
|
||||
return false;
|
||||
}
|
||||
const resultJson = JSON.parse(result.structuredReport.serializedValue);
|
||||
return resultJson?.basic_connectivity?.capable;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
recipe: string;
|
||||
sourceConfigs?: SourceConfig;
|
||||
version?: string | null;
|
||||
selectedSource?: IngestionSource;
|
||||
}
|
||||
|
||||
function TestConnectionButton(props: Props) {
|
||||
const { recipe, sourceConfigs, version } = props;
|
||||
const { recipe, sourceConfigs, version, selectedSource } = props;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [pollingInterval, setPollingInterval] = useState<null | NodeJS.Timeout>(null);
|
||||
const [testConnectionResult, setTestConnectionResult] = useState<null | TestConnectionResult>(null);
|
||||
const [hasEmittedAnalytics, setHasEmittedAnalytics] = useState(false);
|
||||
const [createTestConnectionRequest, { data: requestData }] = useCreateTestConnectionRequestMutation();
|
||||
const [getIngestionExecutionRequest, { data: resultData, loading }] = useGetIngestionExecutionRequestLazyQuery();
|
||||
|
||||
@ -63,6 +80,18 @@ function TestConnectionButton(props: Props) {
|
||||
if (!loading && resultData) {
|
||||
const result = resultData.executionRequest?.result;
|
||||
if (result && result.status !== EXECUTION_REQUEST_STATUS_RUNNING) {
|
||||
const recipeJson = getRecipeJson(recipe);
|
||||
if (recipeJson && !hasEmittedAnalytics) {
|
||||
const basicConnectivity = getBasicConnectivityFromResult(result);
|
||||
analytics.event({
|
||||
type: EventType.IngestionTestConnectionEvent,
|
||||
sourceType: getSourceTypeFromRecipeJson(recipeJson),
|
||||
sourceUrn: selectedSource?.urn,
|
||||
outcome: basicConnectivity ? 'completed' : 'failed',
|
||||
});
|
||||
setHasEmittedAnalytics(true);
|
||||
}
|
||||
|
||||
if (result.status === EXECUTION_REQUEST_STATUS_FAILURE) {
|
||||
message.error(
|
||||
'Something went wrong with your connection test. Please check your recipe and try again.',
|
||||
@ -77,7 +106,7 @@ function TestConnectionButton(props: Props) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
}, [resultData, pollingInterval, loading]);
|
||||
}, [resultData, pollingInterval, loading, recipe, selectedSource?.urn, hasEmittedAnalytics]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isModalVisible && pollingInterval) {
|
||||
@ -88,6 +117,7 @@ function TestConnectionButton(props: Props) {
|
||||
function testConnection() {
|
||||
const recipeJson = getRecipeJson(recipe);
|
||||
if (recipeJson) {
|
||||
setHasEmittedAnalytics(false);
|
||||
createTestConnectionRequest({ variables: { input: { recipe: recipeJson, version } } })
|
||||
.then((res) =>
|
||||
getIngestionExecutionRequest({
|
||||
|
||||
@ -82,7 +82,14 @@ function SourceOption({ source, onClick }: SourceOptionProps) {
|
||||
/**
|
||||
* Component responsible for selecting the mechanism for constructing a new Ingestion Source
|
||||
*/
|
||||
export const SelectTemplateStep = ({ state, updateState, goTo, cancel, ingestionSources }: StepProps) => {
|
||||
export const SelectTemplateStep = ({
|
||||
state,
|
||||
updateState,
|
||||
goTo,
|
||||
cancel,
|
||||
ingestionSources,
|
||||
setSelectedSourceType,
|
||||
}: StepProps) => {
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
|
||||
const onSelectTemplate = (type: string) => {
|
||||
@ -93,6 +100,7 @@ export const SelectTemplateStep = ({ state, updateState, goTo, cancel, ingestion
|
||||
};
|
||||
updateState(newState);
|
||||
goTo(IngestionSourceBuilderStep.DEFINE_RECIPE);
|
||||
setSelectedSourceType?.(type);
|
||||
};
|
||||
|
||||
const filteredSources = ingestionSources.filter(
|
||||
|
||||
@ -38,6 +38,10 @@ export type StepProps = {
|
||||
isEditing: boolean;
|
||||
sourceRefetch?: () => Promise<any>;
|
||||
selectedSource?: IngestionSource;
|
||||
// This is not same as selectedSource
|
||||
// This is required when the ingestion source has not been created
|
||||
selectedSourceType?: string;
|
||||
setSelectedSourceType?: (sourceType: string) => void;
|
||||
};
|
||||
|
||||
export type StringMapEntryInput = {
|
||||
|
||||
@ -55,6 +55,9 @@ public enum DataHubUsageEventType {
|
||||
CREATE_GLOSSARY_ENTITY_EVENT("CreateGlossaryEntityEvent"),
|
||||
CREATE_DOMAIN_EVENT("CreateDomainEvent"),
|
||||
MOVE_DOMAIN_EVENT("MoveDomainEvent"),
|
||||
INGESTION_TEST_CONNECTION_EVENT("IngestionTestConnectionEvent"),
|
||||
INGESTION_EXECUTION_RESULT_VIEWED_EVENT("IngestionExecutionResultViewedEvent"),
|
||||
INGESTION_SOURCE_CONFIGURATION_IMPRESSION_EVENT("IngestionSourceConfigurationImpressionEvent"),
|
||||
CREATE_INGESTION_SOURCE_EVENT("CreateIngestionSourceEvent"),
|
||||
UPDATE_INGESTION_SOURCE_EVENT("UpdateIngestionSourceEvent"),
|
||||
DELETE_INGESTION_SOURCE_EVENT("DeleteIngestionSourceEvent"),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user