From 2903646a15b695659b137f0e0bebd002eaca4add Mon Sep 17 00:00:00 2001 From: Gabe Lyons Date: Mon, 7 Mar 2022 14:30:00 -0800 Subject: [PATCH] feat(impact analysis): bugfixes for Impact Analysis (#4336) --- datahub-web-react/src/app/analytics/event.ts | 11 +- .../styled/search/DownloadAsCsvButton.tsx | 95 +------------- .../styled/search/DownloadAsCsvModal.tsx | 123 ++++++++++++++++++ .../styled/search/SearchExtendedMenu.tsx | 29 +++-- .../styled/search/downloadAsCsvUtil.ts | 11 +- .../src/app/search/SearchResults.tsx | 4 +- .../src/graphql/fragments.graphql | 14 ++ datahub-web-react/src/graphql/search.graphql | 10 ++ 8 files changed, 193 insertions(+), 104 deletions(-) create mode 100644 datahub-web-react/src/app/entity/shared/components/styled/search/DownloadAsCsvModal.tsx diff --git a/datahub-web-react/src/app/analytics/event.ts b/datahub-web-react/src/app/analytics/event.ts index c33e51a4c8baf..e4ca1cf86bcab 100644 --- a/datahub-web-react/src/app/analytics/event.ts +++ b/datahub-web-react/src/app/analytics/event.ts @@ -19,6 +19,7 @@ export enum EventType { RecommendationClickEvent, SearchAcrossLineageEvent, SearchAcrossLineageResultsViewEvent, + DownloadAsCsvEvent, } /** @@ -135,7 +136,6 @@ export const EntityActionType = { UpdateSchemaTerms: 'UpdateSchemaTerms', ClickExternalUrl: 'ClickExternalUrl', }; - export interface EntityActionEvent extends BaseEvent { type: EventType.EntityActionEvent; actionType: string; @@ -176,6 +176,14 @@ export interface SearchAcrossLineageResultsViewEvent extends BaseEvent { total: number; } +export interface DownloadAsCsvEvent extends BaseEvent { + type: EventType.DownloadAsCsvEvent; + query: string; + // optional parameter if its coming from inside an entity page + entityUrn?: string; + path: string; +} + /** * Event consisting of a union of specific event types. */ @@ -193,4 +201,5 @@ export type Event = | RecommendationImpressionEvent | SearchAcrossLineageEvent | SearchAcrossLineageResultsViewEvent + | DownloadAsCsvEvent | RecommendationClickEvent; diff --git a/datahub-web-react/src/app/entity/shared/components/styled/search/DownloadAsCsvButton.tsx b/datahub-web-react/src/app/entity/shared/components/styled/search/DownloadAsCsvButton.tsx index d582b84213538..cb01ff88a2676 100644 --- a/datahub-web-react/src/app/entity/shared/components/styled/search/DownloadAsCsvButton.tsx +++ b/datahub-web-react/src/app/entity/shared/components/styled/search/DownloadAsCsvButton.tsx @@ -1,13 +1,7 @@ -import React, { useState } from 'react'; -import { Button, Input, Modal } from 'antd'; +import React from 'react'; +import { Button } from 'antd'; import { DownloadOutlined } from '@ant-design/icons'; import styled from 'styled-components'; -import { EntityType, FacetFilterInput, SearchAcrossEntitiesInput } from '../../../../../../types.generated'; -import { SearchResultsInterface } from './types'; -import { getSearchCsvDownloadHeader, transformResultsToCsvRow } from './downloadAsCsvUtil'; -import { downloadRowsAsCsv } from '../../../../../search/utils/csvUtils'; -import { useEntityRegistry } from '../../../../../useEntityRegistry'; -import { useEntityData } from '../../../EntityContext'; const DownloadCsvButton = styled(Button)` font-size: 12px; @@ -16,94 +10,17 @@ const DownloadCsvButton = styled(Button)` `; type Props = { - callSearchOnVariables: (variables: { - input: SearchAcrossEntitiesInput; - }) => Promise; - entityFilters: EntityType[]; - filters: FacetFilterInput[]; - query: string; + setShowDownloadAsCsvModal: (showDownloadAsCsvModal: boolean) => any; + isDownloadingCsv: boolean; }; -const SEARCH_PAGE_SIZE_FOR_DOWNLOAD = 1000; - -export default function DownloadAsCsvButton({ callSearchOnVariables, entityFilters, filters, query }: Props) { - const { entityData: entitySearchIsEmbeddedWithin } = useEntityData(); - - const [isDownloadingCsv, setIsDownloadingCsv] = useState(false); - const [showSaveAsModal, setShowSaveAsModal] = useState(false); - const [saveAsTitle, setSaveAsTitle] = useState( - entitySearchIsEmbeddedWithin ? `${entitySearchIsEmbeddedWithin.name}_impact.csv` : 'results.csv', - ); - const entityRegistry = useEntityRegistry(); - - const triggerCsvDownload = (filename) => { - setIsDownloadingCsv(true); - console.log('preparing your csv'); - - let downloadPage = 0; - let accumulatedResults: string[][] = []; - - function fetchNextPage() { - console.log('fetch page number ', downloadPage); - callSearchOnVariables({ - input: { - types: entityFilters, - query, - start: SEARCH_PAGE_SIZE_FOR_DOWNLOAD * downloadPage, - count: SEARCH_PAGE_SIZE_FOR_DOWNLOAD, - filters, - }, - }).then((refetchData) => { - console.log('fetched data for page number ', downloadPage); - accumulatedResults = [ - ...accumulatedResults, - ...transformResultsToCsvRow(refetchData?.searchResults || [], entityRegistry), - ]; - if ((refetchData?.start || 0) + (refetchData?.count || 0) < (refetchData?.total || 0)) { - downloadPage += 1; - fetchNextPage(); - } else { - setIsDownloadingCsv(false); - downloadRowsAsCsv( - getSearchCsvDownloadHeader(refetchData?.searchResults[0]), - accumulatedResults, - filename, - ); - } - }); - } - - fetchNextPage(); - }; - +export default function DownloadAsCsvButton({ setShowDownloadAsCsvModal, isDownloadingCsv }: Props) { return ( <> - setShowSaveAsModal(true)} disabled={isDownloadingCsv}> + setShowDownloadAsCsvModal(true)} disabled={isDownloadingCsv}> {isDownloadingCsv ? 'Downloading...' : 'Download'} - - - - - } - > - setSaveAsTitle(e.target.value)} /> - ); } diff --git a/datahub-web-react/src/app/entity/shared/components/styled/search/DownloadAsCsvModal.tsx b/datahub-web-react/src/app/entity/shared/components/styled/search/DownloadAsCsvModal.tsx new file mode 100644 index 0000000000000..bcd5c6560bd4f --- /dev/null +++ b/datahub-web-react/src/app/entity/shared/components/styled/search/DownloadAsCsvModal.tsx @@ -0,0 +1,123 @@ +import React, { useState } from 'react'; +import { Button, Input, Modal } from 'antd'; +import { useLocation } from 'react-router'; + +import { EntityType, FacetFilterInput, SearchAcrossEntitiesInput } from '../../../../../../types.generated'; +import { SearchResultsInterface } from './types'; +import { getSearchCsvDownloadHeader, transformResultsToCsvRow } from './downloadAsCsvUtil'; +import { downloadRowsAsCsv } from '../../../../../search/utils/csvUtils'; +import { useEntityRegistry } from '../../../../../useEntityRegistry'; +import { useEntityData } from '../../../EntityContext'; +import analytics, { EventType } from '../../../../../analytics'; + +type Props = { + callSearchOnVariables: (variables: { + input: SearchAcrossEntitiesInput; + }) => Promise; + entityFilters: EntityType[]; + filters: FacetFilterInput[]; + query: string; + setIsDownloadingCsv: (isDownloadingCsv: boolean) => any; + showDownloadAsCsvModal: boolean; + setShowDownloadAsCsvModal: (showDownloadAsCsvModal: boolean) => any; +}; + +const SEARCH_PAGE_SIZE_FOR_DOWNLOAD = 1000; + +export default function DownloadAsCsvModal({ + callSearchOnVariables, + entityFilters, + filters, + query, + setIsDownloadingCsv, + showDownloadAsCsvModal, + setShowDownloadAsCsvModal, +}: Props) { + const { entityData: entitySearchIsEmbeddedWithin } = useEntityData(); + const location = useLocation(); + + const [saveAsTitle, setSaveAsTitle] = useState( + entitySearchIsEmbeddedWithin ? `${entitySearchIsEmbeddedWithin.name}_impact.csv` : 'results.csv', + ); + const entityRegistry = useEntityRegistry(); + + const triggerCsvDownload = (filename) => { + setIsDownloadingCsv(true); + console.log('preparing your csv'); + + let downloadPage = 0; + let accumulatedResults: string[][] = []; + + analytics.event({ + type: EventType.DownloadAsCsvEvent, + query, + entityUrn: entitySearchIsEmbeddedWithin?.urn, + path: location.pathname, + }); + + function fetchNextPage() { + console.log('fetch page number ', downloadPage); + callSearchOnVariables({ + input: { + types: entityFilters, + query, + start: SEARCH_PAGE_SIZE_FOR_DOWNLOAD * downloadPage, + count: SEARCH_PAGE_SIZE_FOR_DOWNLOAD, + filters, + }, + }).then((refetchData) => { + console.log('fetched data for page number ', downloadPage); + accumulatedResults = [ + ...accumulatedResults, + ...transformResultsToCsvRow(refetchData?.searchResults || [], entityRegistry), + ]; + if ((refetchData?.start || 0) + (refetchData?.count || 0) < (refetchData?.total || 0)) { + downloadPage += 1; + fetchNextPage(); + } else { + setIsDownloadingCsv(false); + downloadRowsAsCsv( + getSearchCsvDownloadHeader(refetchData?.searchResults[0]), + accumulatedResults, + filename, + ); + } + }); + } + + fetchNextPage(); + }; + + return ( + setShowDownloadAsCsvModal(false)} + title="Download as..." + visible={showDownloadAsCsvModal} + footer={ + <> + + + + } + > + { + setSaveAsTitle(e.target.value); + }} + /> + + ); +} diff --git a/datahub-web-react/src/app/entity/shared/components/styled/search/SearchExtendedMenu.tsx b/datahub-web-react/src/app/entity/shared/components/styled/search/SearchExtendedMenu.tsx index 6c1becba244ba..6e453ae62d5cd 100644 --- a/datahub-web-react/src/app/entity/shared/components/styled/search/SearchExtendedMenu.tsx +++ b/datahub-web-react/src/app/entity/shared/components/styled/search/SearchExtendedMenu.tsx @@ -1,10 +1,11 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Dropdown, Menu } from 'antd'; import { MoreOutlined } from '@ant-design/icons'; import styled from 'styled-components'; import { EntityType, FacetFilterInput, SearchAcrossEntitiesInput } from '../../../../../../types.generated'; import { SearchResultsInterface } from './types'; import DownloadAsCsvButton from './DownloadAsCsvButton'; +import DownloadAsCsvModal from './DownloadAsCsvModal'; const MenuIcon = styled(MoreOutlined)` font-size: 15px; @@ -22,22 +23,34 @@ type Props = { // currently only contains Download As Csv but will be extended to contain other actions as well export default function SearchExtendedMenu({ callSearchOnVariables, entityFilters, filters, query }: Props) { + const [isDownloadingCsv, setIsDownloadingCsv] = useState(false); + const [showDownloadAsCsvModal, setShowDownloadAsCsvModal] = useState(false); + const menu = ( ); return ( - - - + <> + + + + + ); } diff --git a/datahub-web-react/src/app/entity/shared/components/styled/search/downloadAsCsvUtil.ts b/datahub-web-react/src/app/entity/shared/components/styled/search/downloadAsCsvUtil.ts index 08c37e9847b08..dc16c9e07a1e1 100644 --- a/datahub-web-react/src/app/entity/shared/components/styled/search/downloadAsCsvUtil.ts +++ b/datahub-web-react/src/app/entity/shared/components/styled/search/downloadAsCsvUtil.ts @@ -48,7 +48,12 @@ export const transformGenericEntityPropertiesToCsvRow = ( // user owners properties?.ownership?.owners ?.filter((owner) => owner.owner.type === EntityType.CorpUser) - .map((owner) => (owner.owner as CorpUser).properties?.fullName) + .map( + (owner) => + (owner.owner as CorpUser).editableProperties?.displayName || + (owner.owner as CorpUser).properties?.fullName || + (owner.owner as CorpUser).properties?.displayName, + ) .join(',') || '', // user owner emails properties?.ownership?.owners @@ -66,9 +71,7 @@ export const transformGenericEntityPropertiesToCsvRow = ( // group owner emails properties?.ownership?.owners ?.filter((owner) => owner.owner.type === EntityType.CorpGroup) - .map( - (owner) => (owner.owner as CorpGroup).properties?.email || (owner.owner as CorpGroup).properties?.email, - ) + .map((owner) => (owner.owner as CorpGroup).properties?.email) .join(',') || '', // tags properties?.globalTags?.tags?.map((tag) => tag.tag.name).join(',') || '', diff --git a/datahub-web-react/src/app/search/SearchResults.tsx b/datahub-web-react/src/app/search/SearchResults.tsx index 69d4ba964326f..1a28b975a481d 100644 --- a/datahub-web-react/src/app/search/SearchResults.tsx +++ b/datahub-web-react/src/app/search/SearchResults.tsx @@ -78,8 +78,8 @@ const FiltersHeader = styled.div` padding-bottom: 8px; width: 100%; - height: 46px; - line-height: 46px; + height: 47px; + line-height: 47px; border-bottom: 1px solid; border-color: ${(props) => props.theme.styles['border-color-base']}; `; diff --git a/datahub-web-react/src/graphql/fragments.graphql b/datahub-web-react/src/graphql/fragments.graphql index 1f41f77f8e0b4..cfc621383e375 100644 --- a/datahub-web-react/src/graphql/fragments.graphql +++ b/datahub-web-react/src/graphql/fragments.graphql @@ -39,16 +39,30 @@ fragment ownershipFields on Ownership { lastName fullName } + properties { + active + displayName + title + email + firstName + lastName + fullName + } editableProperties { displayName title pictureLink + email } } ... on CorpGroup { urn type name + properties { + displayName + email + } info { displayName email diff --git a/datahub-web-react/src/graphql/search.graphql b/datahub-web-react/src/graphql/search.graphql index 2bfe0f2f08170..20a660a219efd 100644 --- a/datahub-web-react/src/graphql/search.graphql +++ b/datahub-web-react/src/graphql/search.graphql @@ -58,6 +58,15 @@ fragment searchResultFields on Entity { } ... on CorpUser { username + properties { + active + displayName + title + firstName + lastName + fullName + email + } info { active displayName @@ -65,6 +74,7 @@ fragment searchResultFields on Entity { firstName lastName fullName + email } editableProperties { displayName