From 6044d8d2989e6957aaecccc1870c7eb0d8e61a22 Mon Sep 17 00:00:00 2001 From: Aseem Bansal Date: Mon, 1 Sep 2025 17:41:48 +0530 Subject: [PATCH] feat(ui/ingest): add analytics events (#14557) --- datahub-web-react/CLAUDE.md | 13 +++++ datahub-web-react/src/app/analytics/event.ts | 35 +++++++++++- .../app/ingest/source/IngestionSourceList.tsx | 4 ++ .../components/ExecutionDetailsModal.tsx | 29 +++++++++- .../ingestV2/source/IngestionSourceList.tsx | 8 +++ .../source/builder/DefineRecipeStep.tsx | 12 +++- .../builder/IngestionSourceBuilderModal.tsx | 55 +++++++++++++++---- .../ingestV2/source/builder/RecipeBuilder.tsx | 15 ++++- .../source/builder/RecipeForm/RecipeForm.tsx | 14 ++++- .../TestConnection/TestConnectionButton.tsx | 34 +++++++++++- .../source/builder/SelectTemplateStep.tsx | 10 +++- .../src/app/ingestV2/source/builder/types.ts | 4 ++ .../datahubusage/DataHubUsageEventType.java | 3 + 13 files changed, 216 insertions(+), 20 deletions(-) diff --git a/datahub-web-react/CLAUDE.md b/datahub-web-react/CLAUDE.md index 4181215d9d..b3bc7c5e5d 100644 --- a/datahub-web-react/CLAUDE.md +++ b/datahub-web-react/CLAUDE.md @@ -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 diff --git a/datahub-web-react/src/app/analytics/event.ts b/datahub-web-react/src/app/analytics/event.ts index b843bbf5c2..e401a1af02 100644 --- a/datahub-web-react/src/app/analytics/event.ts +++ b/datahub-web-react/src/app/analytics/event.ts @@ -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; diff --git a/datahub-web-react/src/app/ingest/source/IngestionSourceList.tsx b/datahub-web-react/src/app/ingest/source/IngestionSourceList.tsx index 4a054f374a..34353afe6c 100644 --- a/datahub-web-react/src/app/ingest/source/IngestionSourceList.tsx +++ b/datahub-web-react/src/app/ingest/source/IngestionSourceList.tsx @@ -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({ diff --git a/datahub-web-react/src/app/ingestV2/executions/components/ExecutionDetailsModal.tsx b/datahub-web-react/src/app/ingestV2/executions/components/ExecutionDetailsModal.tsx index 14a85f2fa9..c4ef07b0e9 100644 --- a/datahub-web-react/src/app/ingestV2/executions/components/ExecutionDetailsModal.tsx +++ b/datahub-web-react/src/app/ingestV2/executions/components/ExecutionDetailsModal.tsx @@ -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.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) => { setSelectedTab(tab as TabType)} + onChange={(tab) => selectTab(tab as TabType)} getCurrentUrl={() => window.location.pathname} scrollToTopOnChange maxHeight="80vh" diff --git a/datahub-web-react/src/app/ingestV2/source/IngestionSourceList.tsx b/datahub-web-react/src/app/ingestV2/source/IngestionSourceList.tsx index 7ffe41b944..5a65285362 100644 --- a/datahub-web-react/src/app/ingestV2/source/IngestionSourceList.tsx +++ b/datahub-web-react/src/app/ingestV2/source/IngestionSourceList.tsx @@ -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(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 && } diff --git a/datahub-web-react/src/app/ingestV2/source/builder/DefineRecipeStep.tsx b/datahub-web-react/src/app/ingestV2/source/builder/DefineRecipeStep.tsx index 2f9221fd71..2c588b6cf5 100644 --- a/datahub-web-react/src/app/ingestV2/source/builder/DefineRecipeStep.tsx +++ b/datahub-web-react/src/app/ingestV2/source/builder/DefineRecipeStep.tsx @@ -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} /> ); } diff --git a/datahub-web-react/src/app/ingestV2/source/builder/IngestionSourceBuilderModal.tsx b/datahub-web-react/src/app/ingestV2/source/builder/IngestionSourceBuilderModal.tsx index 61dd9a0a33..51200f734e 100644 --- a/datahub-web-react/src/app/ingestV2/source/builder/IngestionSourceBuilderModal.tsx +++ b/datahub-web-react/src/app/ingestV2/source/builder/IngestionSourceBuilderModal.tsx @@ -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; 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} /> diff --git a/datahub-web-react/src/app/ingestV2/source/builder/RecipeBuilder.tsx b/datahub-web-react/src/app/ingestV2/source/builder/RecipeBuilder.tsx index 91e1ef060a..848e3c8ec2 100644 --- a/datahub-web-react/src/app/ingestV2/source/builder/RecipeBuilder.tsx +++ b/datahub-web-react/src/app/ingestV2/source/builder/RecipeBuilder.tsx @@ -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 && ( 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} /> )} diff --git a/datahub-web-react/src/app/ingestV2/source/builder/RecipeForm/TestConnection/TestConnectionButton.tsx b/datahub-web-react/src/app/ingestV2/source/builder/RecipeForm/TestConnection/TestConnectionButton.tsx index 0a9805720c..1f39213ab7 100644 --- a/datahub-web-react/src/app/ingestV2/source/builder/RecipeForm/TestConnection/TestConnectionButton.tsx +++ b/datahub-web-react/src/app/ingestV2/source/builder/RecipeForm/TestConnection/TestConnectionButton.tsx @@ -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); const [testConnectionResult, setTestConnectionResult] = useState(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({ diff --git a/datahub-web-react/src/app/ingestV2/source/builder/SelectTemplateStep.tsx b/datahub-web-react/src/app/ingestV2/source/builder/SelectTemplateStep.tsx index a19cab6eb0..4cbe195b48 100644 --- a/datahub-web-react/src/app/ingestV2/source/builder/SelectTemplateStep.tsx +++ b/datahub-web-react/src/app/ingestV2/source/builder/SelectTemplateStep.tsx @@ -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( diff --git a/datahub-web-react/src/app/ingestV2/source/builder/types.ts b/datahub-web-react/src/app/ingestV2/source/builder/types.ts index d8a4f7c779..5470f61463 100644 --- a/datahub-web-react/src/app/ingestV2/source/builder/types.ts +++ b/datahub-web-react/src/app/ingestV2/source/builder/types.ts @@ -38,6 +38,10 @@ export type StepProps = { isEditing: boolean; sourceRefetch?: () => Promise; 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 = { diff --git a/metadata-service/services/src/main/java/com/linkedin/metadata/datahubusage/DataHubUsageEventType.java b/metadata-service/services/src/main/java/com/linkedin/metadata/datahubusage/DataHubUsageEventType.java index 5ada3a49f1..3abed30f4f 100644 --- a/metadata-service/services/src/main/java/com/linkedin/metadata/datahubusage/DataHubUsageEventType.java +++ b/metadata-service/services/src/main/java/com/linkedin/metadata/datahubusage/DataHubUsageEventType.java @@ -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"),