diff --git a/datahub-web-react/build.gradle b/datahub-web-react/build.gradle index eec093d1af2c7..28835b89c685e 100644 --- a/datahub-web-react/build.gradle +++ b/datahub-web-react/build.gradle @@ -55,6 +55,10 @@ task yarnLint(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) { args = ['run', 'lint'] } +task yarnLintFix(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) { + args = ['run', 'lint-fix'] +} + task yarnBuild(type: YarnTask, dependsOn: [yarnInstall, yarnTest, yarnLint]) { args = ['run', 'build'] } diff --git a/datahub-web-react/src/app/analytics/event.ts b/datahub-web-react/src/app/analytics/event.ts index 0d4545c98f39f..a270bef3817f6 100644 --- a/datahub-web-react/src/app/analytics/event.ts +++ b/datahub-web-react/src/app/analytics/event.ts @@ -122,12 +122,15 @@ export interface EntitySectionViewEvent extends BaseEvent { */ export const EntityActionType = { UpdateTags: 'UpdateTags', + UpdateTerms: 'UpdateTerms', + UpdateLinks: 'UpdateLinks', UpdateOwnership: 'UpdateOwnership', UpdateDocumentation: 'UpdateDocumentation', UpdateDescription: 'UpdateDescription', UpdateProperties: 'UpdateProperties', UpdateSchemaDescription: 'UpdateSchemaDescription', UpdateSchemaTags: 'UpdateSchemaTags', + UpdateSchemaTerms: 'UpdateSchemaTerms', ClickExternalUrl: 'ClickExternalUrl', }; diff --git a/datahub-web-react/src/app/entity/dataset/profile/Documentation.tsx b/datahub-web-react/src/app/entity/dataset/profile/Documentation.tsx index f58526ed11f9d..b23c615fbb72a 100644 --- a/datahub-web-react/src/app/entity/dataset/profile/Documentation.tsx +++ b/datahub-web-react/src/app/entity/dataset/profile/Documentation.tsx @@ -8,6 +8,8 @@ import { InstitutionalMemoryUpdate, } from '../../../../types.generated'; import { useEntityRegistry } from '../../../useEntityRegistry'; +import { useEntityData } from '../../shared/EntityContext'; +import analytics, { EventType, EntityActionType } from '../../../analytics'; export type Props = { authenticatedUserUrn?: string; @@ -93,6 +95,8 @@ export default function Documentation({ setStagedDocs(newStagedDocs); }; + const { urn, entityType } = useEntityData(); + const onSave = async (record: any) => { const row = await form.validateFields(); @@ -113,6 +117,12 @@ export default function Documentation({ }; }); updateDocumentation({ elements: updatedInstitutionalMemory }); + analytics.event({ + type: EventType.EntityActionEvent, + actionType: EntityActionType.UpdateDescription, + entityType, + entityUrn: urn, + }); setEditingIndex(-1); }; @@ -130,6 +140,12 @@ export default function Documentation({ description: doc.description, })); updateDocumentation({ elements: updatedInstitutionalMemory }); + analytics.event({ + type: EventType.EntityActionEvent, + actionType: EntityActionType.UpdateDescription, + entityType, + entityUrn: urn, + }); setStagedDocs(newDocs); }; diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaDescriptionField.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaDescriptionField.tsx index ced481868936f..6edf52b6ac81f 100644 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaDescriptionField.tsx +++ b/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaDescriptionField.tsx @@ -9,6 +9,8 @@ import UpdateDescriptionModal from '../../../../shared/components/legacy/Descrip import StripMarkdownText, { removeMarkdown } from '../../../../shared/components/styled/StripMarkdownText'; import MarkdownViewer from '../../../../shared/components/legacy/MarkdownViewer'; import SchemaEditableContext from '../../../../../shared/SchemaEditableContext'; +import { useEntityData } from '../../../../shared/EntityContext'; +import analytics, { EventType, EntityActionType } from '../../../../../analytics'; const EditIcon = styled(EditOutlined)` cursor: pointer; @@ -89,6 +91,16 @@ export default function DescriptionField({ description, onUpdate, isEdited = fal const [expanded, setExpanded] = useState(!overLimit); const isSchemaEditable = React.useContext(SchemaEditableContext); const onCloseModal = () => setShowAddModal(false); + const { urn, entityType } = useEntityData(); + + const sendAnalytics = () => { + analytics.event({ + type: EventType.EntityActionEvent, + actionType: EntityActionType.UpdateSchemaDescription, + entityType, + entityUrn: urn, + }); + }; const onUpdateModal = async (desc: string | null) => { message.loading({ content: 'Updating...' }); @@ -96,6 +108,7 @@ export default function DescriptionField({ description, onUpdate, isEdited = fal await onUpdate(desc || ''); message.destroy(); message.success({ content: 'Updated!', duration: 2 }); + sendAnalytics(); } catch (e: unknown) { message.destroy(); if (e instanceof Error) message.error({ content: `Update Failed! \n ${e.message || ''}`, duration: 2 }); diff --git a/datahub-web-react/src/app/entity/shared/components/styled/AddLinkModal.tsx b/datahub-web-react/src/app/entity/shared/components/styled/AddLinkModal.tsx index 19f1e19c9a880..3b982fea2ac35 100644 --- a/datahub-web-react/src/app/entity/shared/components/styled/AddLinkModal.tsx +++ b/datahub-web-react/src/app/entity/shared/components/styled/AddLinkModal.tsx @@ -4,6 +4,7 @@ import { PlusOutlined } from '@ant-design/icons'; import { useGetAuthenticatedUser } from '../../../../useGetAuthenticatedUser'; import { useEntityData } from '../../EntityContext'; import { useAddLinkMutation } from '../../../../../graphql/mutations.generated'; +import analytics, { EventType, EntityActionType } from '../../../../analytics'; type AddLinkProps = { buttonProps?: Record; @@ -13,7 +14,7 @@ type AddLinkProps = { export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => { const [isModalVisible, setIsModalVisible] = useState(false); const user = useGetAuthenticatedUser(); - const { urn } = useEntityData(); + const { urn, entityType } = useEntityData(); const [addLinkMutation] = useAddLinkMutation(); const [form] = Form.useForm(); @@ -34,6 +35,12 @@ export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => { variables: { input: { linkUrl: formData.url, label: formData.label, resourceUrn: urn } }, }); message.success({ content: 'Link Added', duration: 2 }); + analytics.event({ + type: EventType.EntityActionEvent, + entityType, + entityUrn: urn, + actionType: EntityActionType.UpdateLinks, + }); } catch (e: unknown) { message.destroy(); if (e instanceof Error) { diff --git a/datahub-web-react/src/app/entity/shared/components/styled/ExpandedOwner.tsx b/datahub-web-react/src/app/entity/shared/components/styled/ExpandedOwner.tsx index 8087fb5d83da8..82851acaa1691 100644 --- a/datahub-web-react/src/app/entity/shared/components/styled/ExpandedOwner.tsx +++ b/datahub-web-react/src/app/entity/shared/components/styled/ExpandedOwner.tsx @@ -7,6 +7,8 @@ import { useRemoveOwnerMutation } from '../../../../../graphql/mutations.generat import { EntityType, Owner } from '../../../../../types.generated'; import { CustomAvatar } from '../../../../shared/avatar'; import { useEntityRegistry } from '../../../../useEntityRegistry'; +import analytics, { EventType, EntityActionType } from '../../../../analytics'; +import { useEntityData } from '../../EntityContext'; type Props = { entityUrn: string; @@ -24,6 +26,7 @@ const OwnerTag = styled(Tag)` export const ExpandedOwner = ({ entityUrn, owner, refetch }: Props) => { const entityRegistry = useEntityRegistry(); + const { entityType } = useEntityData(); const [removeOwnerMutation] = useRemoveOwnerMutation(); let name = ''; @@ -47,6 +50,12 @@ export const ExpandedOwner = ({ entityUrn, owner, refetch }: Props) => { }, }); message.success({ content: 'Owner Removed', duration: 2 }); + analytics.event({ + type: EventType.EntityActionEvent, + actionType: EntityActionType.UpdateOwnership, + entityType, + entityUrn, + }); } catch (e: unknown) { message.destroy(); if (e instanceof Error) { diff --git a/datahub-web-react/src/app/entity/shared/containers/profile/EntityProfile.tsx b/datahub-web-react/src/app/entity/shared/containers/profile/EntityProfile.tsx index f8fbae5e79340..d1ecb74357b34 100644 --- a/datahub-web-react/src/app/entity/shared/containers/profile/EntityProfile.tsx +++ b/datahub-web-react/src/app/entity/shared/containers/profile/EntityProfile.tsx @@ -18,6 +18,7 @@ import { useEntityRegistry } from '../../../../useEntityRegistry'; import LineageExplorer from '../../../../lineage/LineageExplorer'; import CompactContext from '../../../../shared/CompactContext'; import DynamicTab from '../../tabs/Entity/weaklyTypedAspects/DynamicTab'; +import analytics, { EventType } from '../../../../analytics'; type Props = { urn: string; @@ -124,6 +125,12 @@ export const EntityProfile = ({ tabParams?: Record; method?: 'push' | 'replace'; }) => { + analytics.event({ + type: EventType.EntitySectionViewEvent, + entityType, + entityUrn: urn, + section: tabName.toLowerCase(), + }); history[method](getEntityPath(entityType, urn, entityRegistry, false, tabName, tabParams)); }, [history, entityType, urn, entityRegistry], diff --git a/datahub-web-react/src/app/entity/shared/containers/profile/header/EntityHeader.tsx b/datahub-web-react/src/app/entity/shared/containers/profile/header/EntityHeader.tsx index 6fe1f6ee5a370..c85ca6d74db3b 100644 --- a/datahub-web-react/src/app/entity/shared/containers/profile/header/EntityHeader.tsx +++ b/datahub-web-react/src/app/entity/shared/containers/profile/header/EntityHeader.tsx @@ -10,6 +10,7 @@ import { IconStyleType } from '../../../../Entity'; import { ANTD_GRAY } from '../../../constants'; import { useEntityData } from '../../../EntityContext'; import { useEntityPath } from '../utils'; +import analytics, { EventType, EntityActionType } from '../../../../../analytics'; const LogoContainer = styled.span` margin-right: 10px; @@ -105,6 +106,16 @@ export const EntityHeader = () => { const entityPath = useEntityPath(entityType, urn); const externalUrl = entityData?.externalUrl || undefined; const hasExternalUrl = !!externalUrl; + + const sendAnalytics = () => { + analytics.event({ + type: EventType.EntityActionEvent, + actionType: EntityActionType.ClickExternalUrl, + entityType, + entityUrn: urn, + }); + }; + const entityCount = entityData?.entityCount; const typeIcon = entityRegistry.getIcon(entityType, 12, IconStyleType.ACCENT); const container = entityData?.container; @@ -146,7 +157,11 @@ export const EntityHeader = () => { {entityData?.name || ' '} - {hasExternalUrl && View in {platformName}} + {hasExternalUrl && ( + + View in {platformName} + + )}